mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 20:11:09 +00:00
contracts: Refactor the exec module (#8604)
* contracts: Add default implementation for Executable::occupied_storage() * contracts: Refactor the exec module * Let runtime specify the backing type of the call stack This removes the need for a runtime check of the specified `MaxDepth`. We can now garantuee that we don't need to allocate when a new call frame is pushed. * Fix doc typo Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com> * cargo run --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_contracts --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/contracts/src/weights.rs --template=./.maintain/frame-weight-template.hbs * Review nits * Fix defect in contract info caching behaviour * Add more docs * Fix wording and typos Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com> Co-authored-by: Parity Benchmarking Bot <admin@parity.io>
This commit is contained in:
committed by
GitHub
parent
17a1997d18
commit
9e894ce135
Generated
+1
@@ -4799,6 +4799,7 @@ dependencies = [
|
||||
"rand 0.8.3",
|
||||
"rand_pcg 0.3.0",
|
||||
"serde",
|
||||
"smallvec 1.6.1",
|
||||
"sp-core",
|
||||
"sp-io",
|
||||
"sp-runtime",
|
||||
|
||||
@@ -782,7 +782,6 @@ parameter_types! {
|
||||
pub RentFraction: Perbill = Perbill::from_rational(1u32, 30 * DAYS);
|
||||
pub const SurchargeReward: Balance = 150 * MILLICENTS;
|
||||
pub const SignedClaimHandicap: u32 = 2;
|
||||
pub const MaxDepth: u32 = 32;
|
||||
pub const MaxValueSize: u32 = 16 * 1024;
|
||||
// The lazy deletion runs inside on_initialize.
|
||||
pub DeletionWeightLimit: Weight = AVERAGE_ON_INITIALIZE_RATIO *
|
||||
@@ -809,7 +808,7 @@ impl pallet_contracts::Config for Runtime {
|
||||
type DepositPerStorageItem = DepositPerStorageItem;
|
||||
type RentFraction = RentFraction;
|
||||
type SurchargeReward = SurchargeReward;
|
||||
type MaxDepth = MaxDepth;
|
||||
type CallStack = [pallet_contracts::Frame<Self>; 31];
|
||||
type MaxValueSize = MaxValueSize;
|
||||
type WeightPrice = pallet_transaction_payment::Module<Self>;
|
||||
type WeightInfo = pallet_contracts::weights::SubstrateWeight<Self>;
|
||||
|
||||
@@ -18,6 +18,7 @@ log = { version = "0.4", default-features = false }
|
||||
parity-wasm = { version = "0.42", default-features = false }
|
||||
pwasm-utils = { version = "0.17", default-features = false }
|
||||
serde = { version = "1", optional = true, features = ["derive"] }
|
||||
smallvec = { version = "1", default-features = false, features = ["const_generics"] }
|
||||
wasmi-validation = { version = "0.4", default-features = false }
|
||||
|
||||
# Only used in benchmarking to generate random contract code
|
||||
|
||||
@@ -166,16 +166,17 @@ where
|
||||
|
||||
/// Store the supplied storage items into this contracts storage.
|
||||
fn store(&self, items: &Vec<(StorageKey, Vec<u8>)>) -> Result<(), &'static str> {
|
||||
let info = self.alive_info()?;
|
||||
let mut info = self.alive_info()?;
|
||||
for item in items {
|
||||
Storage::<T>::write(
|
||||
&self.account_id,
|
||||
&info.trie_id,
|
||||
<System<T>>::block_number(),
|
||||
&mut info,
|
||||
&item.0,
|
||||
Some(item.1.clone()),
|
||||
)
|
||||
.map_err(|_| "Failed to write storage to restoration dest")?;
|
||||
}
|
||||
<ContractInfoOf<T>>::insert(&self.account_id, ContractInfo::Alive(info.clone()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1148,16 +1149,17 @@ benchmarks! {
|
||||
.. Default::default()
|
||||
});
|
||||
let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
|
||||
let trie_id = instance.alive_info()?.trie_id;
|
||||
let mut info = instance.alive_info()?;
|
||||
for key in keys {
|
||||
Storage::<T>::write(
|
||||
&instance.account_id,
|
||||
&trie_id,
|
||||
<System<T>>::block_number(),
|
||||
&mut info,
|
||||
key.as_slice().try_into().map_err(|e| "Key has wrong length")?,
|
||||
Some(vec![42; T::MaxValueSize::get() as usize])
|
||||
)
|
||||
.map_err(|_| "Failed to write to storage during setup.")?;
|
||||
}
|
||||
<ContractInfoOf<T>>::insert(&instance.account_id, ContractInfo::Alive(info.clone()));
|
||||
let origin = RawOrigin::Signed(instance.caller.clone());
|
||||
}: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
|
||||
|
||||
@@ -1193,16 +1195,17 @@ benchmarks! {
|
||||
.. Default::default()
|
||||
});
|
||||
let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
|
||||
let trie_id = instance.alive_info()?.trie_id;
|
||||
let mut info = instance.alive_info()?;
|
||||
for key in keys {
|
||||
Storage::<T>::write(
|
||||
&instance.account_id,
|
||||
&trie_id,
|
||||
<System<T>>::block_number(),
|
||||
&mut info,
|
||||
key.as_slice().try_into().map_err(|e| "Key has wrong length")?,
|
||||
Some(vec![])
|
||||
)
|
||||
.map_err(|_| "Failed to write to storage during setup.")?;
|
||||
}
|
||||
<ContractInfoOf<T>>::insert(&instance.account_id, ContractInfo::Alive(info.clone()));
|
||||
let origin = RawOrigin::Signed(instance.caller.clone());
|
||||
}: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
|
||||
|
||||
@@ -1238,14 +1241,15 @@ benchmarks! {
|
||||
.. Default::default()
|
||||
});
|
||||
let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
|
||||
let trie_id = instance.alive_info()?.trie_id;
|
||||
let mut info = instance.alive_info()?;
|
||||
Storage::<T>::write(
|
||||
&instance.account_id,
|
||||
&trie_id,
|
||||
<System<T>>::block_number(),
|
||||
&mut info,
|
||||
key.as_slice().try_into().map_err(|e| "Key has wrong length")?,
|
||||
Some(vec![42u8; (n * 1024) as usize])
|
||||
)
|
||||
.map_err(|_| "Failed to write to storage during setup.")?;
|
||||
<ContractInfoOf<T>>::insert(&instance.account_id, ContractInfo::Alive(info.clone()));
|
||||
let origin = RawOrigin::Signed(instance.caller.clone());
|
||||
}: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
|
||||
use crate::{
|
||||
Error,
|
||||
wasm::{Runtime, RuntimeToken},
|
||||
wasm::{Runtime, RuntimeCosts},
|
||||
};
|
||||
use codec::Decode;
|
||||
use frame_support::weights::Weight;
|
||||
@@ -171,7 +171,7 @@ where
|
||||
///
|
||||
/// Weight is synonymous with gas in substrate.
|
||||
pub fn charge_weight(&mut self, amount: Weight) -> Result<()> {
|
||||
self.inner.runtime.charge_gas(RuntimeToken::ChainExtension(amount)).map(|_| ())
|
||||
self.inner.runtime.charge_gas(RuntimeCosts::ChainExtension(amount)).map(|_| ())
|
||||
}
|
||||
|
||||
/// Grants access to the execution environment of the current contract call.
|
||||
@@ -349,7 +349,7 @@ where
|
||||
buffer,
|
||||
allow_skip,
|
||||
|len| {
|
||||
weight_per_byte.map(|w| RuntimeToken::ChainExtension(w.saturating_mul(len.into())))
|
||||
weight_per_byte.map(|w| RuntimeCosts::ChainExtension(w.saturating_mul(len.into())))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -55,12 +55,7 @@ impl<T: Any + Debug + PartialEq + Eq> TestAuxiliaries for T {}
|
||||
/// for consistency). If inlined there should be no observable difference compared
|
||||
/// to a hand-written code.
|
||||
pub trait Token<T: Config>: Copy + Clone + TestAuxiliaries {
|
||||
/// Metadata type, which the token can require for calculating the amount
|
||||
/// of gas to charge. Can be a some configuration type or
|
||||
/// just the `()`.
|
||||
type Metadata;
|
||||
|
||||
/// Calculate amount of gas that should be taken by this token.
|
||||
/// Return the amount of gas that should be taken by this token.
|
||||
///
|
||||
/// This function should be really lightweight and must not fail. It is not
|
||||
/// expected that implementors will query the storage or do any kinds of heavy operations.
|
||||
@@ -68,7 +63,7 @@ pub trait Token<T: Config>: Copy + Clone + TestAuxiliaries {
|
||||
/// That said, implementors of this function still can run into overflows
|
||||
/// while calculating the amount. In this case it is ok to use saturating operations
|
||||
/// since on overflow they will return `max_value` which should consume all gas.
|
||||
fn calculate_amount(&self, metadata: &Self::Metadata) -> Weight;
|
||||
fn weight(&self) -> Weight;
|
||||
}
|
||||
|
||||
/// A wrapper around a type-erased trait object of what used to be a `Token`.
|
||||
@@ -87,6 +82,18 @@ pub struct GasMeter<T: Config> {
|
||||
tokens: Vec<ErasedToken>,
|
||||
}
|
||||
|
||||
impl<T: Config> Default for GasMeter<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
gas_limit: Default::default(),
|
||||
gas_left: Default::default(),
|
||||
_phantom: Default::default(),
|
||||
#[cfg(test)]
|
||||
tokens: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> GasMeter<T>
|
||||
where
|
||||
T::AccountId: UncheckedFrom<<T as frame_system::Config>::Hash> + AsRef<[u8]>
|
||||
@@ -101,6 +108,33 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new gas meter by removing gas from the current meter.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Passing `0` as amount is interpreted as "all remaining gas".
|
||||
pub fn nested(&mut self, amount: Weight) -> Result<Self, DispatchError> {
|
||||
let amount = if amount == 0 {
|
||||
self.gas_left
|
||||
} else {
|
||||
amount
|
||||
};
|
||||
|
||||
// NOTE that it is ok to allocate all available gas since it still ensured
|
||||
// by `charge` that it doesn't reach zero.
|
||||
if self.gas_left < amount {
|
||||
Err(<Error<T>>::OutOfGas.into())
|
||||
} else {
|
||||
self.gas_left = self.gas_left - amount;
|
||||
Ok(GasMeter::new(amount))
|
||||
}
|
||||
}
|
||||
|
||||
/// Absorb the remaining gas of a nested meter after we are done using it.
|
||||
pub fn absorb_nested(&mut self, nested: Self) {
|
||||
self.gas_left += nested.gas_left;
|
||||
}
|
||||
|
||||
/// Account for used gas.
|
||||
///
|
||||
/// Amount is calculated by the given `token`.
|
||||
@@ -111,11 +145,7 @@ where
|
||||
/// NOTE that amount is always consumed, i.e. if there is not enough gas
|
||||
/// then the counter will be set to zero.
|
||||
#[inline]
|
||||
pub fn charge<Tok: Token<T>>(
|
||||
&mut self,
|
||||
metadata: &Tok::Metadata,
|
||||
token: Tok,
|
||||
) -> Result<ChargedAmount, DispatchError> {
|
||||
pub fn charge<Tok: Token<T>>(&mut self, token: Tok) -> Result<ChargedAmount, DispatchError> {
|
||||
#[cfg(test)]
|
||||
{
|
||||
// Unconditionally add the token to the storage.
|
||||
@@ -126,7 +156,7 @@ where
|
||||
self.tokens.push(erased_tok);
|
||||
}
|
||||
|
||||
let amount = token.calculate_amount(metadata);
|
||||
let amount = token.weight();
|
||||
let new_value = self.gas_left.checked_sub(amount);
|
||||
|
||||
// We always consume the gas even if there is not enough gas.
|
||||
@@ -142,13 +172,8 @@ where
|
||||
///
|
||||
/// This is when a maximum a priori amount was charged and then should be partially
|
||||
/// refunded to match the actual amount.
|
||||
pub fn adjust_gas<Tok: Token<T>>(
|
||||
&mut self,
|
||||
charged_amount: ChargedAmount,
|
||||
metadata: &Tok::Metadata,
|
||||
token: Tok,
|
||||
) {
|
||||
let adjustment = charged_amount.0.saturating_sub(token.calculate_amount(metadata));
|
||||
pub fn adjust_gas<Tok: Token<T>>(&mut self, charged_amount: ChargedAmount, token: Tok) {
|
||||
let adjustment = charged_amount.0.saturating_sub(token.weight());
|
||||
self.gas_left = self.gas_left.saturating_add(adjustment).min(self.gas_limit);
|
||||
}
|
||||
|
||||
@@ -161,34 +186,6 @@ where
|
||||
self.gas_left = self.gas_left.saturating_add(amount.0).min(self.gas_limit)
|
||||
}
|
||||
|
||||
/// Allocate some amount of gas and perform some work with
|
||||
/// a newly created nested gas meter.
|
||||
///
|
||||
/// Invokes `f` with either the gas meter that has `amount` gas left or
|
||||
/// with `None`, if this gas meter has not enough gas to allocate given `amount`.
|
||||
///
|
||||
/// All unused gas in the nested gas meter is returned to this gas meter.
|
||||
pub fn with_nested<R, F: FnOnce(Option<&mut GasMeter<T>>) -> R>(
|
||||
&mut self,
|
||||
amount: Weight,
|
||||
f: F,
|
||||
) -> R {
|
||||
// NOTE that it is ok to allocate all available gas since it still ensured
|
||||
// by `charge` that it doesn't reach zero.
|
||||
if self.gas_left < amount {
|
||||
f(None)
|
||||
} else {
|
||||
self.gas_left = self.gas_left - amount;
|
||||
let mut nested = GasMeter::new(amount);
|
||||
|
||||
let r = f(Some(&mut nested));
|
||||
|
||||
self.gas_left = self.gas_left + nested.gas_left;
|
||||
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns how much gas was used.
|
||||
pub fn gas_spent(&self) -> Weight {
|
||||
self.gas_limit - self.gas_left
|
||||
@@ -269,24 +266,7 @@ mod tests {
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
struct SimpleToken(u64);
|
||||
impl Token<Test> for SimpleToken {
|
||||
type Metadata = ();
|
||||
fn calculate_amount(&self, _metadata: &()) -> u64 { self.0 }
|
||||
}
|
||||
|
||||
struct MultiplierTokenMetadata {
|
||||
multiplier: u64,
|
||||
}
|
||||
/// A simple token that charges for the given amount multiplied to
|
||||
/// a multiplier taken from a given metadata.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
struct MultiplierToken(u64);
|
||||
|
||||
impl Token<Test> for MultiplierToken {
|
||||
type Metadata = MultiplierTokenMetadata;
|
||||
fn calculate_amount(&self, metadata: &MultiplierTokenMetadata) -> u64 {
|
||||
// Probably you want to use saturating mul in production code.
|
||||
self.0 * metadata.multiplier
|
||||
}
|
||||
fn weight(&self) -> u64 { self.0 }
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -295,34 +275,20 @@ mod tests {
|
||||
assert_eq!(gas_meter.gas_left(), 50000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple() {
|
||||
let mut gas_meter = GasMeter::<Test>::new(50000);
|
||||
|
||||
let result = gas_meter
|
||||
.charge(&MultiplierTokenMetadata { multiplier: 3 }, MultiplierToken(10));
|
||||
assert!(!result.is_err());
|
||||
|
||||
assert_eq!(gas_meter.gas_left(), 49_970);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tracing() {
|
||||
let mut gas_meter = GasMeter::<Test>::new(50000);
|
||||
assert!(!gas_meter.charge(&(), SimpleToken(1)).is_err());
|
||||
assert!(!gas_meter
|
||||
.charge(&MultiplierTokenMetadata { multiplier: 3 }, MultiplierToken(10))
|
||||
.is_err());
|
||||
assert!(!gas_meter.charge(SimpleToken(1)).is_err());
|
||||
|
||||
let mut tokens = gas_meter.tokens()[0..2].iter();
|
||||
match_tokens!(tokens, SimpleToken(1), MultiplierToken(10),);
|
||||
let mut tokens = gas_meter.tokens().iter();
|
||||
match_tokens!(tokens, SimpleToken(1),);
|
||||
}
|
||||
|
||||
// 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>::new(0);
|
||||
assert!(gas_meter.charge(&(), SimpleToken(1)).is_err());
|
||||
assert!(gas_meter.charge(SimpleToken(1)).is_err());
|
||||
}
|
||||
|
||||
// Make sure that if the gas meter is charged by exceeding amount then not only an error
|
||||
@@ -335,10 +301,10 @@ mod tests {
|
||||
let mut gas_meter = GasMeter::<Test>::new(200);
|
||||
|
||||
// The first charge is should lead to OOG.
|
||||
assert!(gas_meter.charge(&(), SimpleToken(300)).is_err());
|
||||
assert!(gas_meter.charge(SimpleToken(300)).is_err());
|
||||
|
||||
// The gas meter is emptied at this moment, so this should also fail.
|
||||
assert!(gas_meter.charge(&(), SimpleToken(1)).is_err());
|
||||
assert!(gas_meter.charge(SimpleToken(1)).is_err());
|
||||
}
|
||||
|
||||
|
||||
@@ -347,6 +313,6 @@ mod tests {
|
||||
#[test]
|
||||
fn charge_exact_amount() {
|
||||
let mut gas_meter = GasMeter::<Test>::new(25);
|
||||
assert!(!gas_meter.charge(&(), SimpleToken(25)).is_err());
|
||||
assert!(!gas_meter.charge(SimpleToken(25)).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,10 +103,10 @@ pub mod weights;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub use crate::{pallet::*, schedule::Schedule};
|
||||
pub use crate::{pallet::*, schedule::Schedule, exec::Frame};
|
||||
use crate::{
|
||||
gas::GasMeter,
|
||||
exec::{ExecutionContext, Executable},
|
||||
exec::{Stack as ExecStack, Executable},
|
||||
rent::Rent,
|
||||
storage::{Storage, DeletedContract, ContractInfo, AliveContractInfo, TombstoneContractInfo},
|
||||
weights::WeightInfo,
|
||||
@@ -210,9 +210,12 @@ pub mod pallet {
|
||||
#[pallet::constant]
|
||||
type SurchargeReward: Get<BalanceOf<Self>>;
|
||||
|
||||
/// The maximum nesting level of a call/instantiate stack.
|
||||
#[pallet::constant]
|
||||
type MaxDepth: Get<u32>;
|
||||
/// The type of the call stack determines the maximum nesting depth of contract calls.
|
||||
///
|
||||
/// The allowed depth is `CallStack::size() + 1`.
|
||||
/// Therefore a size of `0` means that a contract cannot use call or instantiate.
|
||||
/// In other words only the origin called "root contract" is allowed to execute then.
|
||||
type CallStack: smallvec::Array<Item=Frame<Self>>;
|
||||
|
||||
/// The maximum size of a storage value and event payload in bytes.
|
||||
#[pallet::constant]
|
||||
@@ -313,8 +316,9 @@ pub mod pallet {
|
||||
let dest = T::Lookup::lookup(dest)?;
|
||||
let mut gas_meter = GasMeter::new(gas_limit);
|
||||
let schedule = <CurrentSchedule<T>>::get();
|
||||
let mut ctx = ExecutionContext::<T, PrefabWasmModule<T>>::top_level(origin, &schedule);
|
||||
let (result, code_len) = match ctx.call(dest, value, &mut gas_meter, data) {
|
||||
let (result, code_len) = match ExecStack::<T, PrefabWasmModule<T>>::run_call(
|
||||
origin, dest, &mut gas_meter, &schedule, value, data
|
||||
) {
|
||||
Ok((output, len)) => (Ok(output), len),
|
||||
Err((err, len)) => (Err(err), len),
|
||||
};
|
||||
@@ -365,9 +369,9 @@ pub mod pallet {
|
||||
let executable = PrefabWasmModule::from_code(code, &schedule)?;
|
||||
let code_len = executable.code_len();
|
||||
ensure!(code_len <= T::MaxCodeSize::get(), Error::<T>::CodeTooLarge);
|
||||
let mut ctx = ExecutionContext::<T, PrefabWasmModule<T>>::top_level(origin, &schedule);
|
||||
let result = ctx.instantiate(endowment, &mut gas_meter, executable, data, &salt)
|
||||
.map(|(_address, output)| output);
|
||||
let result = ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
|
||||
origin, executable, &mut gas_meter, &schedule, endowment, data, &salt,
|
||||
).map(|(_address, output)| output);
|
||||
gas_meter.into_dispatch_result(
|
||||
result,
|
||||
T::WeightInfo::instantiate_with_code(code_len / 1024, salt.len() as u32 / 1024)
|
||||
@@ -395,10 +399,10 @@ pub mod pallet {
|
||||
let mut gas_meter = GasMeter::new(gas_limit);
|
||||
let schedule = <CurrentSchedule<T>>::get();
|
||||
let executable = PrefabWasmModule::from_storage(code_hash, &schedule, &mut gas_meter)?;
|
||||
let mut ctx = ExecutionContext::<T, PrefabWasmModule<T>>::top_level(origin, &schedule);
|
||||
let code_len = executable.code_len();
|
||||
let result = ctx.instantiate(endowment, &mut gas_meter, executable, data, &salt)
|
||||
.map(|(_address, output)| output);
|
||||
let result = ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
|
||||
origin, executable, &mut gas_meter, &schedule, endowment, data, &salt,
|
||||
).map(|(_address, output)| output);
|
||||
gas_meter.into_dispatch_result(
|
||||
result,
|
||||
T::WeightInfo::instantiate(code_len / 1024, salt.len() as u32 / 1024),
|
||||
@@ -606,6 +610,10 @@ pub mod pallet {
|
||||
StorageExhausted,
|
||||
/// A contract with the same AccountId already exists.
|
||||
DuplicateContract,
|
||||
/// A contract self destructed in its constructor.
|
||||
///
|
||||
/// This can be triggered by a call to `seal_terminate` or `seal_restore_to`.
|
||||
TerminatedInConstructor,
|
||||
}
|
||||
|
||||
/// Current cost schedule for contracts.
|
||||
@@ -680,8 +688,9 @@ where
|
||||
) -> ContractExecResult {
|
||||
let mut gas_meter = GasMeter::new(gas_limit);
|
||||
let schedule = <CurrentSchedule<T>>::get();
|
||||
let mut ctx = ExecutionContext::<T, PrefabWasmModule<T>>::top_level(origin, &schedule);
|
||||
let result = ctx.call(dest, value, &mut gas_meter, input_data);
|
||||
let result = ExecStack::<T, PrefabWasmModule<T>>::run_call(
|
||||
origin, dest, &mut gas_meter, &schedule, value, input_data,
|
||||
);
|
||||
let gas_consumed = gas_meter.gas_spent();
|
||||
ContractExecResult {
|
||||
result: result.map(|r| r.0).map_err(|r| r.0.error),
|
||||
@@ -711,7 +720,6 @@ where
|
||||
) -> ContractInstantiateResult<T::AccountId, T::BlockNumber> {
|
||||
let mut gas_meter = GasMeter::new(gas_limit);
|
||||
let schedule = <CurrentSchedule<T>>::get();
|
||||
let mut ctx = ExecutionContext::<T, PrefabWasmModule<T>>::top_level(origin, &schedule);
|
||||
let executable = match code {
|
||||
Code::Upload(Bytes(binary)) => PrefabWasmModule::from_code(binary, &schedule),
|
||||
Code::Existing(hash) => PrefabWasmModule::from_storage(hash, &schedule, &mut gas_meter),
|
||||
@@ -724,20 +732,21 @@ where
|
||||
debug_message: Bytes(Vec::new()),
|
||||
}
|
||||
};
|
||||
let result = ctx.instantiate(endowment, &mut gas_meter, executable, data, &salt)
|
||||
.and_then(|(account_id, result)| {
|
||||
let rent_projection = if compute_projection {
|
||||
Some(Rent::<T, PrefabWasmModule<T>>::compute_projection(&account_id)
|
||||
.map_err(|_| <Error<T>>::NewContractNotFunded)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let result = ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
|
||||
origin, executable, &mut gas_meter, &schedule, endowment, data, &salt,
|
||||
).and_then(|(account_id, result)| {
|
||||
let rent_projection = if compute_projection {
|
||||
Some(Rent::<T, PrefabWasmModule<T>>::compute_projection(&account_id)
|
||||
.map_err(|_| <Error<T>>::NewContractNotFunded)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(InstantiateReturnValue {
|
||||
result,
|
||||
account_id,
|
||||
rent_projection,
|
||||
})
|
||||
Ok(InstantiateReturnValue {
|
||||
result,
|
||||
account_id,
|
||||
rent_projection,
|
||||
})
|
||||
});
|
||||
ContractInstantiateResult {
|
||||
result: result.map_err(|e| e.error),
|
||||
|
||||
@@ -460,16 +460,13 @@ where
|
||||
///
|
||||
/// Result<(CallerCodeSize, DestCodeSize), (DispatchError, CallerCodeSize, DestCodesize)>
|
||||
pub fn restore_to(
|
||||
origin: T::AccountId,
|
||||
origin: &T::AccountId,
|
||||
mut origin_contract: AliveContractInfo<T>,
|
||||
dest: T::AccountId,
|
||||
code_hash: CodeHash<T>,
|
||||
rent_allowance: BalanceOf<T>,
|
||||
delta: Vec<crate::exec::StorageKey>,
|
||||
) -> Result<(u32, u32), (DispatchError, u32, u32)> {
|
||||
let mut origin_contract = <ContractInfoOf<T>>::get(&origin)
|
||||
.and_then(|c| c.get_alive())
|
||||
.ok_or((Error::<T>::InvalidSourceContract.into(), 0, 0))?;
|
||||
|
||||
let child_trie_info = origin_contract.child_trie_info();
|
||||
|
||||
let current_block = <frame_system::Pallet<T>>::block_number();
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
|
||||
use crate::{
|
||||
exec::{AccountIdOf, StorageKey},
|
||||
BalanceOf, CodeHash, ContractInfoOf, Config, TrieId,
|
||||
AccountCounter, DeletionQueue, Error,
|
||||
BalanceOf, CodeHash, ContractInfoOf, Config, TrieId, DeletionQueue, Error,
|
||||
weights::WeightInfo,
|
||||
};
|
||||
use codec::{Codec, Encode, Decode};
|
||||
@@ -61,7 +60,9 @@ impl<T: Config> ContractInfo<T> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// If contract is alive then return some reference to alive info
|
||||
#[cfg(test)]
|
||||
pub fn as_alive(&self) -> Option<&AliveContractInfo<T>> {
|
||||
if let ContractInfo::Alive(ref alive) = self {
|
||||
Some(alive)
|
||||
@@ -144,11 +145,6 @@ impl<T: Config> From<AliveContractInfo<T>> for ContractInfo<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// An error that means that the account requested either doesn't exist or represents a tombstone
|
||||
/// account.
|
||||
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
||||
pub struct ContractAbsentError;
|
||||
|
||||
#[derive(Encode, Decode)]
|
||||
pub struct DeletedContract {
|
||||
pair_count: u32,
|
||||
@@ -177,25 +173,14 @@ where
|
||||
/// This function also updates the bookkeeping info such as: number of total non-empty pairs a
|
||||
/// contract owns, the last block the storage was written to, etc. That's why, in contrast to
|
||||
/// `read`, this function also requires the `account` ID.
|
||||
///
|
||||
/// If the contract specified by the id `account` doesn't exist `Err` is returned.`
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics iff the `account` specified is not alive and in storage.
|
||||
pub fn write(
|
||||
account: &AccountIdOf<T>,
|
||||
trie_id: &TrieId,
|
||||
block_number: T::BlockNumber,
|
||||
new_info: &mut AliveContractInfo<T>,
|
||||
key: &StorageKey,
|
||||
opt_new_value: Option<Vec<u8>>,
|
||||
) -> DispatchResult {
|
||||
let mut new_info = match <ContractInfoOf<T>>::get(account) {
|
||||
Some(ContractInfo::Alive(alive)) => alive,
|
||||
None | Some(ContractInfo::Tombstone(_)) => panic!("Contract not found"),
|
||||
};
|
||||
|
||||
let hashed_key = blake2_256(key);
|
||||
let child_trie_info = &child_trie_info(&trie_id);
|
||||
let child_trie_info = &child_trie_info(&new_info.trie_id);
|
||||
|
||||
let opt_prev_len = child::len(&child_trie_info, &hashed_key);
|
||||
|
||||
@@ -225,8 +210,7 @@ where
|
||||
.and_then(|val| val.checked_add(new_value_len))
|
||||
.ok_or_else(|| Error::<T>::StorageExhausted)?;
|
||||
|
||||
new_info.last_write = Some(<frame_system::Pallet<T>>::block_number());
|
||||
<ContractInfoOf<T>>::insert(&account, ContractInfo::Alive(new_info));
|
||||
new_info.last_write = Some(block_number);
|
||||
|
||||
// Finally, perform the change on the storage.
|
||||
match opt_new_value {
|
||||
@@ -237,65 +221,35 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the rent allowance set for the contract give by the account id.
|
||||
pub fn rent_allowance(
|
||||
account: &AccountIdOf<T>,
|
||||
) -> Result<BalanceOf<T>, ContractAbsentError>
|
||||
{
|
||||
<ContractInfoOf<T>>::get(account)
|
||||
.and_then(|i| i.as_alive().map(|i| i.rent_allowance))
|
||||
.ok_or(ContractAbsentError)
|
||||
}
|
||||
|
||||
/// Set the rent allowance for the contract given by the account id.
|
||||
///
|
||||
/// Returns `Err` if the contract doesn't exist or is a tombstone.
|
||||
pub fn set_rent_allowance(
|
||||
account: &AccountIdOf<T>,
|
||||
rent_allowance: BalanceOf<T>,
|
||||
) -> Result<(), ContractAbsentError> {
|
||||
<ContractInfoOf<T>>::mutate(account, |maybe_contract_info| match maybe_contract_info {
|
||||
Some(ContractInfo::Alive(ref mut alive_info)) => {
|
||||
alive_info.rent_allowance = rent_allowance;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(ContractAbsentError),
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a new contract descriptor in the storage with the given code hash at the given address.
|
||||
///
|
||||
/// Returns `Err` if there is already a contract (or a tombstone) exists at the given address.
|
||||
pub fn place_contract(
|
||||
pub fn new_contract(
|
||||
account: &AccountIdOf<T>,
|
||||
trie_id: TrieId,
|
||||
ch: CodeHash<T>,
|
||||
) -> Result<AliveContractInfo<T>, DispatchError> {
|
||||
<ContractInfoOf<T>>::try_mutate(account, |existing| {
|
||||
if existing.is_some() {
|
||||
return Err(Error::<T>::DuplicateContract.into());
|
||||
}
|
||||
if <ContractInfoOf<T>>::contains_key(account) {
|
||||
return Err(Error::<T>::DuplicateContract.into());
|
||||
}
|
||||
|
||||
let contract = AliveContractInfo::<T> {
|
||||
code_hash: ch,
|
||||
storage_size: 0,
|
||||
trie_id,
|
||||
deduct_block:
|
||||
// We want to charge rent for the first block in advance. Therefore we
|
||||
// treat the contract as if it was created in the last block and then
|
||||
// charge rent for it during instantiation.
|
||||
<frame_system::Pallet<T>>::block_number().saturating_sub(1u32.into()),
|
||||
rent_allowance: <BalanceOf<T>>::max_value(),
|
||||
rent_payed: <BalanceOf<T>>::zero(),
|
||||
pair_count: 0,
|
||||
last_write: None,
|
||||
_reserved: None,
|
||||
};
|
||||
let contract = AliveContractInfo::<T> {
|
||||
code_hash: ch,
|
||||
storage_size: 0,
|
||||
trie_id,
|
||||
deduct_block:
|
||||
// We want to charge rent for the first block in advance. Therefore we
|
||||
// treat the contract as if it was created in the last block and then
|
||||
// charge rent for it during instantiation.
|
||||
<frame_system::Pallet<T>>::block_number().saturating_sub(1u32.into()),
|
||||
rent_allowance: <BalanceOf<T>>::max_value(),
|
||||
rent_payed: <BalanceOf<T>>::zero(),
|
||||
pair_count: 0,
|
||||
last_write: None,
|
||||
_reserved: None,
|
||||
};
|
||||
|
||||
*existing = Some(contract.clone().into());
|
||||
|
||||
Ok(contract)
|
||||
})
|
||||
Ok(contract)
|
||||
}
|
||||
|
||||
/// Push a contract's trie to the deletion queue for lazy removal.
|
||||
@@ -397,16 +351,9 @@ where
|
||||
|
||||
/// This generator uses inner counter for account id and applies the hash over `AccountId +
|
||||
/// accountid_counter`.
|
||||
pub fn generate_trie_id(account_id: &AccountIdOf<T>) -> TrieId {
|
||||
// Note that skipping a value due to error is not an issue here.
|
||||
// We only need uniqueness, not sequence.
|
||||
let new_seed = <AccountCounter<T>>::mutate(|v| {
|
||||
*v = v.wrapping_add(1);
|
||||
*v
|
||||
});
|
||||
|
||||
pub fn generate_trie_id(account_id: &AccountIdOf<T>, seed: u64) -> TrieId {
|
||||
let buf: Vec<_> = account_id.as_ref().iter()
|
||||
.chain(&new_seed.to_le_bytes())
|
||||
.chain(&seed.to_le_bytes())
|
||||
.cloned()
|
||||
.collect();
|
||||
T::Hashing::hash(&buf).as_ref().into()
|
||||
@@ -414,11 +361,10 @@ where
|
||||
|
||||
/// Returns the code hash of the contract specified by `account` ID.
|
||||
#[cfg(test)]
|
||||
pub fn code_hash(account: &AccountIdOf<T>) -> Result<CodeHash<T>, ContractAbsentError>
|
||||
pub fn code_hash(account: &AccountIdOf<T>) -> Option<CodeHash<T>>
|
||||
{
|
||||
<ContractInfoOf<T>>::get(account)
|
||||
.and_then(|i| i.as_alive().map(|i| i.code_hash))
|
||||
.ok_or(ContractAbsentError)
|
||||
}
|
||||
|
||||
/// Fill up the queue in order to exercise the limits during testing.
|
||||
|
||||
@@ -23,7 +23,7 @@ use crate::{
|
||||
Result as ExtensionResult, Environment, ChainExtension, Ext, SysConfig, RetVal,
|
||||
UncheckedFrom, InitState, ReturnFlags,
|
||||
},
|
||||
exec::{AccountIdOf, Executable}, wasm::PrefabWasmModule,
|
||||
exec::{AccountIdOf, Executable, Frame}, wasm::PrefabWasmModule,
|
||||
weights::WeightInfo,
|
||||
wasm::ReturnCode as RuntimeReturnCode,
|
||||
storage::RawAliveContractInfo,
|
||||
@@ -69,27 +69,37 @@ frame_support::construct_runtime!(
|
||||
|
||||
#[macro_use]
|
||||
pub mod test_utils {
|
||||
use super::{Test, Balances};
|
||||
use super::{Test, Balances, System};
|
||||
use crate::{
|
||||
ContractInfoOf, CodeHash,
|
||||
storage::Storage,
|
||||
storage::{Storage, ContractInfo},
|
||||
exec::{StorageKey, AccountIdOf},
|
||||
Pallet as Contracts,
|
||||
TrieId, AccountCounter,
|
||||
};
|
||||
use frame_support::traits::Currency;
|
||||
|
||||
pub fn set_storage(addr: &AccountIdOf<Test>, key: &StorageKey, value: Option<Vec<u8>>) {
|
||||
let contract_info = <ContractInfoOf::<Test>>::get(&addr).unwrap().get_alive().unwrap();
|
||||
Storage::<Test>::write(addr, &contract_info.trie_id, key, value).unwrap();
|
||||
let mut contract_info = <ContractInfoOf::<Test>>::get(&addr).unwrap().get_alive().unwrap();
|
||||
let block_number = System::block_number();
|
||||
Storage::<Test>::write(block_number, &mut contract_info, key, value).unwrap();
|
||||
}
|
||||
pub fn get_storage(addr: &AccountIdOf<Test>, key: &StorageKey) -> Option<Vec<u8>> {
|
||||
let contract_info = <ContractInfoOf::<Test>>::get(&addr).unwrap().get_alive().unwrap();
|
||||
Storage::<Test>::read(&contract_info.trie_id, key)
|
||||
}
|
||||
pub fn generate_trie_id(address: &AccountIdOf<Test>) -> TrieId {
|
||||
let seed = <AccountCounter<Test>>::mutate(|counter| {
|
||||
*counter += 1;
|
||||
*counter
|
||||
});
|
||||
Storage::<Test>::generate_trie_id(address, seed)
|
||||
}
|
||||
pub fn place_contract(address: &AccountIdOf<Test>, code_hash: CodeHash<Test>) {
|
||||
let trie_id = Storage::<Test>::generate_trie_id(address);
|
||||
let trie_id = generate_trie_id(address);
|
||||
set_balance(address, Contracts::<Test>::subsistence_threshold() * 10);
|
||||
Storage::<Test>::place_contract(&address, trie_id, code_hash).unwrap();
|
||||
let contract = Storage::<Test>::new_contract(&address, trie_id, code_hash).unwrap();
|
||||
<ContractInfoOf<Test>>::insert(address, ContractInfo::Alive(contract));
|
||||
}
|
||||
pub fn set_balance(who: &AccountIdOf<Test>, amount: u64) {
|
||||
let imbalance = Balances::deposit_creating(who, amount);
|
||||
@@ -251,7 +261,6 @@ parameter_types! {
|
||||
pub const DepositPerStorageItem: u64 = 10_000;
|
||||
pub RentFraction: Perbill = Perbill::from_rational(4u32, 10_000u32);
|
||||
pub const SurchargeReward: u64 = 500_000;
|
||||
pub const MaxDepth: u32 = 100;
|
||||
pub const MaxValueSize: u32 = 16_384;
|
||||
pub const DeletionQueueDepth: u32 = 1024;
|
||||
pub const DeletionWeightLimit: Weight = 500_000_000_000;
|
||||
@@ -281,7 +290,7 @@ impl Config for Test {
|
||||
type DepositPerStorageItem = DepositPerStorageItem;
|
||||
type RentFraction = RentFraction;
|
||||
type SurchargeReward = SurchargeReward;
|
||||
type MaxDepth = MaxDepth;
|
||||
type CallStack = [Frame<Self>; 31];
|
||||
type MaxValueSize = MaxValueSize;
|
||||
type WeightPrice = Self;
|
||||
type WeightInfo = ();
|
||||
@@ -379,8 +388,8 @@ fn account_removal_does_not_remove_storage() {
|
||||
use self::test_utils::{set_storage, get_storage};
|
||||
|
||||
ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
|
||||
let trie_id1 = Storage::<Test>::generate_trie_id(&ALICE);
|
||||
let trie_id2 = Storage::<Test>::generate_trie_id(&BOB);
|
||||
let trie_id1 = test_utils::generate_trie_id(&ALICE);
|
||||
let trie_id2 = test_utils::generate_trie_id(&BOB);
|
||||
let key1 = &[1; 32];
|
||||
let key2 = &[2; 32];
|
||||
|
||||
@@ -1835,7 +1844,7 @@ fn cannot_self_destruct_in_constructor() {
|
||||
vec![],
|
||||
vec![],
|
||||
),
|
||||
Error::<Test>::NotCallable,
|
||||
Error::<Test>::TerminatedInConstructor,
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -2326,18 +2335,18 @@ fn lazy_removal_partial_remove_works() {
|
||||
);
|
||||
|
||||
let addr = Contracts::contract_address(&ALICE, &hash, &[]);
|
||||
let info = <ContractInfoOf::<Test>>::get(&addr).unwrap().get_alive().unwrap();
|
||||
let trie = &info.child_trie_info();
|
||||
let mut info = <ContractInfoOf::<Test>>::get(&addr).unwrap().get_alive().unwrap();
|
||||
|
||||
// Put value into the contracts child trie
|
||||
for val in &vals {
|
||||
Storage::<Test>::write(
|
||||
&addr,
|
||||
&info.trie_id,
|
||||
System::block_number(),
|
||||
&mut info,
|
||||
&val.0,
|
||||
Some(val.2.clone()),
|
||||
).unwrap();
|
||||
}
|
||||
<ContractInfoOf<Test>>::insert(&addr, ContractInfo::Alive(info.clone()));
|
||||
|
||||
// Terminate the contract
|
||||
assert_ok!(Contracts::call(
|
||||
@@ -2351,9 +2360,11 @@ fn lazy_removal_partial_remove_works() {
|
||||
// Contract info should be gone
|
||||
assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
|
||||
|
||||
let trie = info.child_trie_info();
|
||||
|
||||
// But value should be still there as the lazy removal did not run, yet.
|
||||
for val in &vals {
|
||||
assert_eq!(child::get::<u32>(trie, &blake2_256(&val.0)), Some(val.1));
|
||||
assert_eq!(child::get::<u32>(&trie, &blake2_256(&val.0)), Some(val.1));
|
||||
}
|
||||
|
||||
trie.clone()
|
||||
@@ -2407,8 +2418,7 @@ fn lazy_removal_does_no_run_on_full_block() {
|
||||
);
|
||||
|
||||
let addr = Contracts::contract_address(&ALICE, &hash, &[]);
|
||||
let info = <ContractInfoOf::<Test>>::get(&addr).unwrap().get_alive().unwrap();
|
||||
let trie = &info.child_trie_info();
|
||||
let mut info = <ContractInfoOf::<Test>>::get(&addr).unwrap().get_alive().unwrap();
|
||||
let max_keys = 30;
|
||||
|
||||
// Create some storage items for the contract.
|
||||
@@ -2420,12 +2430,13 @@ fn lazy_removal_does_no_run_on_full_block() {
|
||||
// Put value into the contracts child trie
|
||||
for val in &vals {
|
||||
Storage::<Test>::write(
|
||||
&addr,
|
||||
&info.trie_id,
|
||||
System::block_number(),
|
||||
&mut info,
|
||||
&val.0,
|
||||
Some(val.2.clone()),
|
||||
).unwrap();
|
||||
}
|
||||
<ContractInfoOf<Test>>::insert(&addr, ContractInfo::Alive(info.clone()));
|
||||
|
||||
// Terminate the contract
|
||||
assert_ok!(Contracts::call(
|
||||
@@ -2439,9 +2450,11 @@ fn lazy_removal_does_no_run_on_full_block() {
|
||||
// Contract info should be gone
|
||||
assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
|
||||
|
||||
let trie = info.child_trie_info();
|
||||
|
||||
// But value should be still there as the lazy removal did not run, yet.
|
||||
for val in &vals {
|
||||
assert_eq!(child::get::<u32>(trie, &blake2_256(&val.0)), Some(val.1));
|
||||
assert_eq!(child::get::<u32>(&trie, &blake2_256(&val.0)), Some(val.1));
|
||||
}
|
||||
|
||||
// Fill up the block which should prevent the lazy storage removal from running.
|
||||
@@ -2458,7 +2471,7 @@ fn lazy_removal_does_no_run_on_full_block() {
|
||||
|
||||
// All the keys are still in place
|
||||
for val in &vals {
|
||||
assert_eq!(child::get::<u32>(trie, &blake2_256(&val.0)), Some(val.1));
|
||||
assert_eq!(child::get::<u32>(&trie, &blake2_256(&val.0)), Some(val.1));
|
||||
}
|
||||
|
||||
// Run the lazy removal directly which disregards the block limits
|
||||
@@ -2466,7 +2479,7 @@ fn lazy_removal_does_no_run_on_full_block() {
|
||||
|
||||
// Now the keys should be gone
|
||||
for val in &vals {
|
||||
assert_eq!(child::get::<u32>(trie, &blake2_256(&val.0)), None);
|
||||
assert_eq!(child::get::<u32>(&trie, &blake2_256(&val.0)), None);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2491,8 +2504,7 @@ fn lazy_removal_does_not_use_all_weight() {
|
||||
);
|
||||
|
||||
let addr = Contracts::contract_address(&ALICE, &hash, &[]);
|
||||
let info = <ContractInfoOf::<Test>>::get(&addr).unwrap().get_alive().unwrap();
|
||||
let trie = &info.child_trie_info();
|
||||
let mut info = <ContractInfoOf::<Test>>::get(&addr).unwrap().get_alive().unwrap();
|
||||
let weight_limit = 5_000_000_000;
|
||||
let (weight_per_key, max_keys) = Storage::<Test>::deletion_budget(1, weight_limit);
|
||||
|
||||
@@ -2505,12 +2517,13 @@ fn lazy_removal_does_not_use_all_weight() {
|
||||
// Put value into the contracts child trie
|
||||
for val in &vals {
|
||||
Storage::<Test>::write(
|
||||
&addr,
|
||||
&info.trie_id,
|
||||
System::block_number(),
|
||||
&mut info,
|
||||
&val.0,
|
||||
Some(val.2.clone()),
|
||||
).unwrap();
|
||||
}
|
||||
<ContractInfoOf<Test>>::insert(&addr, ContractInfo::Alive(info.clone()));
|
||||
|
||||
// Terminate the contract
|
||||
assert_ok!(Contracts::call(
|
||||
@@ -2524,9 +2537,11 @@ fn lazy_removal_does_not_use_all_weight() {
|
||||
// Contract info should be gone
|
||||
assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
|
||||
|
||||
let trie = info.child_trie_info();
|
||||
|
||||
// But value should be still there as the lazy removal did not run, yet.
|
||||
for val in &vals {
|
||||
assert_eq!(child::get::<u32>(trie, &blake2_256(&val.0)), Some(val.1));
|
||||
assert_eq!(child::get::<u32>(&trie, &blake2_256(&val.0)), Some(val.1));
|
||||
}
|
||||
|
||||
// Run the lazy removal
|
||||
@@ -2537,7 +2552,7 @@ fn lazy_removal_does_not_use_all_weight() {
|
||||
|
||||
// All the keys are removed
|
||||
for val in vals {
|
||||
assert_eq!(child::get::<u32>(trie, &blake2_256(&val.0)), None);
|
||||
assert_eq!(child::get::<u32>(&trie, &blake2_256(&val.0)), None);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ where
|
||||
// in the storage.
|
||||
//
|
||||
// We need to re-instrument the code with the latest schedule here.
|
||||
gas_meter.charge(&(), InstrumentToken(prefab_module.original_code_len))?;
|
||||
gas_meter.charge(InstrumentToken(prefab_module.original_code_len))?;
|
||||
private::reinstrument(&mut prefab_module, schedule)?;
|
||||
}
|
||||
}
|
||||
@@ -194,9 +194,7 @@ fn increment_64(refcount: &mut u64) {
|
||||
struct InstrumentToken(u32);
|
||||
|
||||
impl<T: Config> Token<T> for InstrumentToken {
|
||||
type Metadata = ();
|
||||
|
||||
fn calculate_amount(&self, _metadata: &Self::Metadata) -> Weight {
|
||||
fn weight(&self) -> Weight {
|
||||
T::WeightInfo::instrument(self.0 / 1024)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ use sp_std::prelude::*;
|
||||
use sp_core::crypto::UncheckedFrom;
|
||||
use codec::{Encode, Decode};
|
||||
use frame_support::dispatch::DispatchError;
|
||||
pub use self::runtime::{ReturnCode, Runtime, RuntimeToken};
|
||||
pub use self::runtime::{ReturnCode, Runtime, RuntimeCosts};
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
pub use self::code_cache::reinstrument;
|
||||
#[cfg(test)]
|
||||
@@ -172,10 +172,9 @@ where
|
||||
|
||||
fn execute<E: Ext<T = T>>(
|
||||
self,
|
||||
mut ext: E,
|
||||
ext: &mut E,
|
||||
function: &ExportedFunction,
|
||||
input_data: Vec<u8>,
|
||||
gas_meter: &mut GasMeter<E::T>,
|
||||
) -> ExecResult {
|
||||
let memory =
|
||||
sp_sandbox::Memory::new(self.initial, Some(self.maximum))
|
||||
@@ -196,10 +195,9 @@ where
|
||||
});
|
||||
|
||||
let mut runtime = Runtime::new(
|
||||
&mut ext,
|
||||
ext,
|
||||
input_data,
|
||||
memory,
|
||||
gas_meter,
|
||||
);
|
||||
|
||||
// We store before executing so that the code hash is available in the constructor.
|
||||
@@ -220,13 +218,6 @@ where
|
||||
&self.code_hash
|
||||
}
|
||||
|
||||
fn occupied_storage(&self) -> u32 {
|
||||
// We disregard the size of the struct itself as the size is completely
|
||||
// dominated by the code size.
|
||||
let len = self.aggregate_code_len();
|
||||
len.checked_div(self.refcount as u32).unwrap_or(len)
|
||||
}
|
||||
|
||||
fn code_len(&self) -> u32 {
|
||||
self.code.len() as u32
|
||||
}
|
||||
@@ -260,8 +251,7 @@ mod tests {
|
||||
use assert_matches::assert_matches;
|
||||
use pallet_contracts_primitives::{ExecReturnValue, ReturnFlags};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
const GAS_LIMIT: Weight = 10_000_000_000;
|
||||
use sp_std::borrow::BorrowMut;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct DispatchEntry(Call);
|
||||
@@ -295,7 +285,6 @@ mod tests {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MockExt {
|
||||
storage: HashMap<StorageKey, Vec<u8>>,
|
||||
rent_allowance: u64,
|
||||
@@ -307,23 +296,48 @@ mod tests {
|
||||
events: Vec<(Vec<H256>, Vec<u8>)>,
|
||||
schedule: Schedule<Test>,
|
||||
rent_params: RentParams<Test>,
|
||||
gas_meter: GasMeter<Test>,
|
||||
}
|
||||
|
||||
impl Default for MockExt {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
storage: Default::default(),
|
||||
rent_allowance: Default::default(),
|
||||
instantiates: Default::default(),
|
||||
terminations: Default::default(),
|
||||
transfers: Default::default(),
|
||||
restores: Default::default(),
|
||||
events: Default::default(),
|
||||
schedule: Default::default(),
|
||||
rent_params: Default::default(),
|
||||
gas_meter: GasMeter::new(10_000_000_000),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ext for MockExt {
|
||||
type T = Test;
|
||||
|
||||
fn get_storage(&self, key: &StorageKey) -> Option<Vec<u8>> {
|
||||
self.storage.get(key).cloned()
|
||||
}
|
||||
fn set_storage(&mut self, key: StorageKey, value: Option<Vec<u8>>) -> DispatchResult {
|
||||
*self.storage.entry(key).or_insert(Vec::new()) = value.unwrap_or(Vec::new());
|
||||
Ok(())
|
||||
fn call(
|
||||
&mut self,
|
||||
_gas_limit: Weight,
|
||||
to: AccountIdOf<Self::T>,
|
||||
value: u64,
|
||||
data: Vec<u8>,
|
||||
) -> Result<(ExecReturnValue, u32), (ExecError, u32)> {
|
||||
self.transfers.push(TransferEntry {
|
||||
to,
|
||||
value,
|
||||
data: data,
|
||||
});
|
||||
Ok((ExecReturnValue { flags: ReturnFlags::empty(), data: Bytes(Vec::new()) }, 0))
|
||||
}
|
||||
fn instantiate(
|
||||
&mut self,
|
||||
gas_limit: Weight,
|
||||
code_hash: CodeHash<Test>,
|
||||
endowment: u64,
|
||||
gas_meter: &mut GasMeter<Test>,
|
||||
data: Vec<u8>,
|
||||
salt: &[u8],
|
||||
) -> Result<(AccountIdOf<Self::T>, ExecReturnValue, u32), (ExecError, u32)> {
|
||||
@@ -331,7 +345,7 @@ mod tests {
|
||||
code_hash: code_hash.clone(),
|
||||
endowment,
|
||||
data: data.to_vec(),
|
||||
gas_left: gas_meter.gas_left(),
|
||||
gas_left: gas_limit,
|
||||
salt: salt.to_vec(),
|
||||
});
|
||||
Ok((
|
||||
@@ -355,22 +369,6 @@ mod tests {
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
fn call(
|
||||
&mut self,
|
||||
to: &AccountIdOf<Self::T>,
|
||||
value: u64,
|
||||
_gas_meter: &mut GasMeter<Test>,
|
||||
data: Vec<u8>,
|
||||
) -> Result<(ExecReturnValue, u32), (ExecError, u32)> {
|
||||
self.transfers.push(TransferEntry {
|
||||
to: to.clone(),
|
||||
value,
|
||||
data: data,
|
||||
});
|
||||
// Assume for now that it was just a plain transfer.
|
||||
// TODO: Add tests for different call outcomes.
|
||||
Ok((ExecReturnValue { flags: ReturnFlags::empty(), data: Bytes(Vec::new()) }, 0))
|
||||
}
|
||||
fn terminate(
|
||||
&mut self,
|
||||
beneficiary: &AccountIdOf<Self::T>,
|
||||
@@ -395,6 +393,13 @@ mod tests {
|
||||
});
|
||||
Ok((0, 0))
|
||||
}
|
||||
fn get_storage(&mut self, key: &StorageKey) -> Option<Vec<u8>> {
|
||||
self.storage.get(key).cloned()
|
||||
}
|
||||
fn set_storage(&mut self, key: StorageKey, value: Option<Vec<u8>>) -> DispatchResult {
|
||||
*self.storage.entry(key).or_insert(Vec::new()) = value.unwrap_or(Vec::new());
|
||||
Ok(())
|
||||
}
|
||||
fn caller(&self) -> &AccountIdOf<Self::T> {
|
||||
&ALICE
|
||||
}
|
||||
@@ -425,7 +430,7 @@ mod tests {
|
||||
fn set_rent_allowance(&mut self, rent_allowance: u64) {
|
||||
self.rent_allowance = rent_allowance;
|
||||
}
|
||||
fn rent_allowance(&self) -> u64 {
|
||||
fn rent_allowance(&mut self) -> u64 {
|
||||
self.rent_allowance
|
||||
}
|
||||
fn block_number(&self) -> u64 { 121 }
|
||||
@@ -439,127 +444,22 @@ mod tests {
|
||||
fn rent_params(&self) -> &RentParams<Self::T> {
|
||||
&self.rent_params
|
||||
}
|
||||
}
|
||||
|
||||
impl Ext for &mut MockExt {
|
||||
type T = <MockExt as Ext>::T;
|
||||
|
||||
fn get_storage(&self, key: &[u8; 32]) -> Option<Vec<u8>> {
|
||||
(**self).get_storage(key)
|
||||
}
|
||||
fn set_storage(&mut self, key: [u8; 32], value: Option<Vec<u8>>) -> DispatchResult {
|
||||
(**self).set_storage(key, value)
|
||||
}
|
||||
fn instantiate(
|
||||
&mut self,
|
||||
code: CodeHash<Test>,
|
||||
value: u64,
|
||||
gas_meter: &mut GasMeter<Test>,
|
||||
input_data: Vec<u8>,
|
||||
salt: &[u8],
|
||||
) -> Result<(AccountIdOf<Self::T>, ExecReturnValue, u32), (ExecError, u32)> {
|
||||
(**self).instantiate(code, value, gas_meter, input_data, salt)
|
||||
}
|
||||
fn transfer(
|
||||
&mut self,
|
||||
to: &AccountIdOf<Self::T>,
|
||||
value: u64,
|
||||
) -> Result<(), DispatchError> {
|
||||
(**self).transfer(to, value)
|
||||
}
|
||||
fn terminate(
|
||||
&mut self,
|
||||
beneficiary: &AccountIdOf<Self::T>,
|
||||
) -> Result<u32, (DispatchError, u32)> {
|
||||
(**self).terminate(beneficiary)
|
||||
}
|
||||
fn call(
|
||||
&mut self,
|
||||
to: &AccountIdOf<Self::T>,
|
||||
value: u64,
|
||||
gas_meter: &mut GasMeter<Test>,
|
||||
input_data: Vec<u8>,
|
||||
) -> Result<(ExecReturnValue, u32), (ExecError, u32)> {
|
||||
(**self).call(to, value, gas_meter, input_data)
|
||||
}
|
||||
fn restore_to(
|
||||
&mut self,
|
||||
dest: AccountIdOf<Self::T>,
|
||||
code_hash: H256,
|
||||
rent_allowance: u64,
|
||||
delta: Vec<StorageKey>,
|
||||
) -> Result<(u32, u32), (DispatchError, u32, u32)> {
|
||||
(**self).restore_to(
|
||||
dest,
|
||||
code_hash,
|
||||
rent_allowance,
|
||||
delta,
|
||||
)
|
||||
}
|
||||
fn caller(&self) -> &AccountIdOf<Self::T> {
|
||||
(**self).caller()
|
||||
}
|
||||
fn address(&self) -> &AccountIdOf<Self::T> {
|
||||
(**self).address()
|
||||
}
|
||||
fn balance(&self) -> u64 {
|
||||
(**self).balance()
|
||||
}
|
||||
fn value_transferred(&self) -> u64 {
|
||||
(**self).value_transferred()
|
||||
}
|
||||
fn now(&self) -> &u64 {
|
||||
(**self).now()
|
||||
}
|
||||
fn minimum_balance(&self) -> u64 {
|
||||
(**self).minimum_balance()
|
||||
}
|
||||
fn tombstone_deposit(&self) -> u64 {
|
||||
(**self).tombstone_deposit()
|
||||
}
|
||||
fn random(&self, subject: &[u8]) -> (SeedOf<Self::T>, BlockNumberOf<Self::T>) {
|
||||
(**self).random(subject)
|
||||
}
|
||||
fn deposit_event(&mut self, topics: Vec<H256>, data: Vec<u8>) {
|
||||
(**self).deposit_event(topics, data)
|
||||
}
|
||||
fn set_rent_allowance(&mut self, rent_allowance: u64) {
|
||||
(**self).set_rent_allowance(rent_allowance)
|
||||
}
|
||||
fn rent_allowance(&self) -> u64 {
|
||||
(**self).rent_allowance()
|
||||
}
|
||||
fn block_number(&self) -> u64 {
|
||||
(**self).block_number()
|
||||
}
|
||||
fn max_value_size(&self) -> u32 {
|
||||
(**self).max_value_size()
|
||||
}
|
||||
fn get_weight_price(&self, weight: Weight) -> BalanceOf<Self::T> {
|
||||
(**self).get_weight_price(weight)
|
||||
}
|
||||
fn schedule(&self) -> &Schedule<Self::T> {
|
||||
(**self).schedule()
|
||||
}
|
||||
fn rent_params(&self) -> &RentParams<Self::T> {
|
||||
(**self).rent_params()
|
||||
fn gas_meter(&mut self) -> &mut GasMeter<Self::T> {
|
||||
&mut self.gas_meter
|
||||
}
|
||||
}
|
||||
|
||||
fn execute<E: Ext>(
|
||||
fn execute<E: BorrowMut<MockExt>>(
|
||||
wat: &str,
|
||||
input_data: Vec<u8>,
|
||||
ext: E,
|
||||
gas_meter: &mut GasMeter<E::T>,
|
||||
mut ext: E,
|
||||
) -> ExecResult
|
||||
where
|
||||
<E::T as frame_system::Config>::AccountId:
|
||||
UncheckedFrom<<E::T as frame_system::Config>::Hash> + AsRef<[u8]>
|
||||
{
|
||||
let wasm = wat::parse_str(wat).unwrap();
|
||||
let schedule = crate::Schedule::default();
|
||||
let executable = PrefabWasmModule::<E::T>::from_code(wasm, &schedule).unwrap();
|
||||
executable.execute(ext, &ExportedFunction::Call, input_data, gas_meter)
|
||||
let executable = PrefabWasmModule::<<MockExt as Ext>::T>::from_code(wasm, &schedule)
|
||||
.unwrap();
|
||||
executable.execute(ext.borrow_mut(), &ExportedFunction::Call, input_data)
|
||||
}
|
||||
|
||||
const CODE_TRANSFER: &str = r#"
|
||||
@@ -603,7 +503,6 @@ mod tests {
|
||||
CODE_TRANSFER,
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
&mut GasMeter::new(GAS_LIMIT),
|
||||
));
|
||||
|
||||
assert_eq!(
|
||||
@@ -669,7 +568,6 @@ mod tests {
|
||||
CODE_CALL,
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
&mut GasMeter::new(GAS_LIMIT),
|
||||
));
|
||||
|
||||
assert_eq!(
|
||||
@@ -745,7 +643,6 @@ mod tests {
|
||||
CODE_INSTANTIATE,
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
&mut GasMeter::new(GAS_LIMIT),
|
||||
));
|
||||
|
||||
assert_matches!(
|
||||
@@ -794,7 +691,6 @@ mod tests {
|
||||
CODE_TERMINATE,
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
&mut GasMeter::new(GAS_LIMIT),
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
@@ -857,7 +753,6 @@ mod tests {
|
||||
&CODE_TRANSFER_LIMITED_GAS,
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
&mut GasMeter::new(GAS_LIMIT),
|
||||
));
|
||||
|
||||
assert_eq!(
|
||||
@@ -945,7 +840,6 @@ mod tests {
|
||||
CODE_GET_STORAGE,
|
||||
vec![],
|
||||
mock_ext,
|
||||
&mut GasMeter::new(GAS_LIMIT),
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(output, ExecReturnValue {
|
||||
@@ -1003,7 +897,6 @@ mod tests {
|
||||
CODE_CALLER,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut GasMeter::new(GAS_LIMIT),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1056,7 +949,6 @@ mod tests {
|
||||
CODE_ADDRESS,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut GasMeter::new(GAS_LIMIT),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1103,12 +995,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn balance() {
|
||||
let mut gas_meter = GasMeter::new(GAS_LIMIT);
|
||||
assert_ok!(execute(
|
||||
CODE_BALANCE,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut gas_meter,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1155,12 +1045,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn gas_price() {
|
||||
let mut gas_meter = GasMeter::new(GAS_LIMIT);
|
||||
assert_ok!(execute(
|
||||
CODE_GAS_PRICE,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut gas_meter,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1205,18 +1093,19 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn gas_left() {
|
||||
let mut gas_meter = GasMeter::new(GAS_LIMIT);
|
||||
let mut ext = MockExt::default();
|
||||
let gas_limit = ext.gas_meter.gas_left();
|
||||
|
||||
let output = execute(
|
||||
CODE_GAS_LEFT,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut gas_meter,
|
||||
&mut ext,
|
||||
).unwrap();
|
||||
|
||||
let gas_left = Weight::decode(&mut &*output.data).unwrap();
|
||||
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");
|
||||
let actual_left = ext.gas_meter.gas_left();
|
||||
assert!(gas_left < gas_limit, "gas_left must be less than initial");
|
||||
assert!(gas_left > actual_left, "gas_left must be greater than final");
|
||||
}
|
||||
|
||||
const CODE_VALUE_TRANSFERRED: &str = r#"
|
||||
@@ -1262,12 +1151,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn value_transferred() {
|
||||
let mut gas_meter = GasMeter::new(GAS_LIMIT);
|
||||
assert_ok!(execute(
|
||||
CODE_VALUE_TRANSFERRED,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut gas_meter,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1301,7 +1188,6 @@ mod tests {
|
||||
CODE_RETURN_FROM_START_FN,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut GasMeter::new(GAS_LIMIT),
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
@@ -1356,12 +1242,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn now() {
|
||||
let mut gas_meter = GasMeter::new(GAS_LIMIT);
|
||||
assert_ok!(execute(
|
||||
CODE_TIMESTAMP_NOW,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut gas_meter,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1407,12 +1291,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn minimum_balance() {
|
||||
let mut gas_meter = GasMeter::new(GAS_LIMIT);
|
||||
assert_ok!(execute(
|
||||
CODE_MINIMUM_BALANCE,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut gas_meter,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1458,12 +1340,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn tombstone_deposit() {
|
||||
let mut gas_meter = GasMeter::new(GAS_LIMIT);
|
||||
assert_ok!(execute(
|
||||
CODE_TOMBSTONE_DEPOSIT,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut gas_meter,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1523,13 +1403,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn random() {
|
||||
let mut gas_meter = GasMeter::new(GAS_LIMIT);
|
||||
|
||||
let output = execute(
|
||||
CODE_RANDOM,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut gas_meter,
|
||||
).unwrap();
|
||||
|
||||
// The mock ext just returns the same data that was passed as the subject.
|
||||
@@ -1601,13 +1478,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn random_v1() {
|
||||
let mut gas_meter = GasMeter::new(GAS_LIMIT);
|
||||
|
||||
let output = execute(
|
||||
CODE_RANDOM_V1,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut gas_meter,
|
||||
).unwrap();
|
||||
|
||||
// The mock ext just returns the same data that was passed as the subject.
|
||||
@@ -1650,12 +1524,10 @@ mod tests {
|
||||
#[test]
|
||||
fn deposit_event() {
|
||||
let mut mock_ext = MockExt::default();
|
||||
let mut gas_meter = GasMeter::new(GAS_LIMIT);
|
||||
assert_ok!(execute(
|
||||
CODE_DEPOSIT_EVENT,
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
&mut gas_meter
|
||||
));
|
||||
|
||||
assert_eq!(mock_ext.events, vec![
|
||||
@@ -1663,7 +1535,7 @@ mod tests {
|
||||
vec![0x00, 0x01, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x14, 0x00])
|
||||
]);
|
||||
|
||||
assert!(gas_meter.gas_left() > 0);
|
||||
assert!(mock_ext.gas_meter.gas_left() > 0);
|
||||
}
|
||||
|
||||
const CODE_DEPOSIT_EVENT_MAX_TOPICS: &str = r#"
|
||||
@@ -1693,17 +1565,14 @@ mod tests {
|
||||
)
|
||||
"#;
|
||||
|
||||
/// Checks that the runtime traps if there are more than `max_topic_events` topics.
|
||||
#[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::new(GAS_LIMIT);
|
||||
|
||||
assert_eq!(
|
||||
execute(
|
||||
CODE_DEPOSIT_EVENT_MAX_TOPICS,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut gas_meter
|
||||
),
|
||||
Err(ExecError {
|
||||
error: Error::<Test>::TooManyTopics.into(),
|
||||
@@ -1738,17 +1607,14 @@ mod tests {
|
||||
)
|
||||
"#;
|
||||
|
||||
/// Checks that the runtime traps if there are duplicates.
|
||||
#[test]
|
||||
fn deposit_event_duplicates() {
|
||||
// Checks that the runtime traps if there are duplicates.
|
||||
let mut gas_meter = GasMeter::new(GAS_LIMIT);
|
||||
|
||||
assert_eq!(
|
||||
execute(
|
||||
CODE_DEPOSIT_EVENT_DUPLICATES,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut gas_meter
|
||||
),
|
||||
Err(ExecError {
|
||||
error: Error::<Test>::DuplicateTopics.into(),
|
||||
@@ -1806,7 +1672,6 @@ mod tests {
|
||||
CODE_BLOCK_NUMBER,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut GasMeter::new(GAS_LIMIT),
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
@@ -1848,7 +1713,6 @@ mod tests {
|
||||
CODE_RETURN_WITH_DATA,
|
||||
hex!("00000000445566778899").to_vec(),
|
||||
MockExt::default(),
|
||||
&mut GasMeter::new(GAS_LIMIT),
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(output, ExecReturnValue {
|
||||
@@ -1864,7 +1728,6 @@ mod tests {
|
||||
CODE_RETURN_WITH_DATA,
|
||||
hex!("010000005566778899").to_vec(),
|
||||
MockExt::default(),
|
||||
&mut GasMeter::new(GAS_LIMIT),
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(output, ExecReturnValue {
|
||||
@@ -1897,7 +1760,6 @@ mod tests {
|
||||
CODE_OUT_OF_BOUNDS_ACCESS,
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
&mut GasMeter::new(GAS_LIMIT),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@@ -1932,7 +1794,6 @@ mod tests {
|
||||
CODE_DECODE_FAILURE,
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
&mut GasMeter::new(GAS_LIMIT),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@@ -1980,7 +1841,6 @@ mod tests {
|
||||
CODE_RENT_PARAMS,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut GasMeter::new(GAS_LIMIT),
|
||||
).unwrap();
|
||||
let rent_params = Bytes(<RentParams<Test>>::default().encode());
|
||||
assert_eq!(output, ExecReturnValue { flags: ReturnFlags::empty(), data: rent_params });
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
use crate::{
|
||||
Config, CodeHash, BalanceOf, Error,
|
||||
exec::{Ext, StorageKey, TopicOf, ExecResult, ExecError},
|
||||
gas::{GasMeter, Token, ChargedAmount},
|
||||
gas::{Token, ChargedAmount},
|
||||
wasm::env_def::ConvertibleToWasm,
|
||||
schedule::HostFnWeights,
|
||||
};
|
||||
@@ -28,7 +28,6 @@ use parity_wasm::elements::ValueType;
|
||||
use frame_support::{dispatch::DispatchError, ensure, traits::Get, weights::Weight};
|
||||
use sp_std::prelude::*;
|
||||
use codec::{Decode, DecodeAll, Encode};
|
||||
use sp_runtime::traits::SaturatedConversion;
|
||||
use sp_core::{Bytes, crypto::UncheckedFrom};
|
||||
use sp_io::hashing::{
|
||||
keccak_256,
|
||||
@@ -132,7 +131,7 @@ impl<T: Into<DispatchError>> From<T> for TrapReason {
|
||||
|
||||
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum RuntimeToken {
|
||||
pub enum RuntimeCosts {
|
||||
/// Charge the gas meter with the cost of a metering block. The charged costs are
|
||||
/// the supplied cost of the block plus the overhead of the metering itself.
|
||||
MeteringBlock(u32),
|
||||
@@ -220,15 +219,14 @@ pub enum RuntimeToken {
|
||||
RentParams,
|
||||
}
|
||||
|
||||
impl<T: Config> Token<T> for RuntimeToken
|
||||
where
|
||||
T::AccountId: UncheckedFrom<T::Hash>, T::AccountId: AsRef<[u8]>
|
||||
{
|
||||
type Metadata = HostFnWeights<T>;
|
||||
|
||||
fn calculate_amount(&self, s: &Self::Metadata) -> Weight {
|
||||
use self::RuntimeToken::*;
|
||||
match *self {
|
||||
impl RuntimeCosts {
|
||||
fn token<T>(&self, s: &HostFnWeights<T>) -> RuntimeToken
|
||||
where
|
||||
T: Config,
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
|
||||
{
|
||||
use self::RuntimeCosts::*;
|
||||
let weight = match *self {
|
||||
MeteringBlock(amount) => s.gas.saturating_add(amount.into()),
|
||||
Caller => s.caller,
|
||||
Address => s.address,
|
||||
@@ -287,14 +285,37 @@ where
|
||||
ChainExtension(amount) => amount,
|
||||
CopyIn(len) => s.return_per_byte.saturating_mul(len.into()),
|
||||
RentParams => s.rent_params,
|
||||
};
|
||||
RuntimeToken {
|
||||
#[cfg(test)]
|
||||
_created_from: *self,
|
||||
weight,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Copy, Clone)]
|
||||
struct RuntimeToken {
|
||||
#[cfg(test)]
|
||||
_created_from: RuntimeCosts,
|
||||
weight: Weight,
|
||||
}
|
||||
|
||||
impl<T> Token<T> for RuntimeToken
|
||||
where
|
||||
T: Config,
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
|
||||
{
|
||||
fn weight(&self) -> Weight {
|
||||
self.weight
|
||||
}
|
||||
}
|
||||
|
||||
/// This is only appropriate when writing out data of constant size that does not depend on user
|
||||
/// input. In this case the costs for this copy was already charged as part of the token at
|
||||
/// the beginning of the API entry point.
|
||||
fn already_charged(_: u32) -> Option<RuntimeToken> {
|
||||
fn already_charged(_: u32) -> Option<RuntimeCosts> {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -303,7 +324,6 @@ pub struct Runtime<'a, E: Ext + 'a> {
|
||||
ext: &'a mut E,
|
||||
input_data: Option<Vec<u8>>,
|
||||
memory: sp_sandbox::Memory,
|
||||
gas_meter: &'a mut GasMeter<E::T>,
|
||||
trap_reason: Option<TrapReason>,
|
||||
}
|
||||
|
||||
@@ -317,13 +337,11 @@ where
|
||||
ext: &'a mut E,
|
||||
input_data: Vec<u8>,
|
||||
memory: sp_sandbox::Memory,
|
||||
gas_meter: &'a mut GasMeter<E::T>,
|
||||
) -> Self {
|
||||
Runtime {
|
||||
ext,
|
||||
input_data: Some(input_data),
|
||||
memory,
|
||||
gas_meter,
|
||||
trap_reason: None,
|
||||
}
|
||||
}
|
||||
@@ -406,21 +424,16 @@ where
|
||||
/// Charge the gas meter with the specified token.
|
||||
///
|
||||
/// Returns `Err(HostError)` if there is not enough gas.
|
||||
pub fn charge_gas<Tok>(&mut self, token: Tok) -> Result<ChargedAmount, DispatchError>
|
||||
where
|
||||
Tok: Token<E::T, Metadata=HostFnWeights<E::T>>,
|
||||
{
|
||||
self.gas_meter.charge(&self.ext.schedule().host_fn_weights, token)
|
||||
pub fn charge_gas(&mut self, costs: RuntimeCosts) -> Result<ChargedAmount, DispatchError> {
|
||||
let token = costs.token(&self.ext.schedule().host_fn_weights);
|
||||
self.ext.gas_meter().charge(token)
|
||||
}
|
||||
|
||||
/// Correct previously charged gas amount.
|
||||
pub fn adjust_gas<Tok>(&mut self, charged_amount: ChargedAmount, adjusted_amount: Tok)
|
||||
where
|
||||
Tok: Token<E::T, Metadata=HostFnWeights<E::T>>,
|
||||
{
|
||||
self.gas_meter.adjust_gas(
|
||||
pub fn adjust_gas(&mut self, charged_amount: ChargedAmount, adjusted_amount: RuntimeCosts) {
|
||||
let adjusted_amount = adjusted_amount.token(&self.ext.schedule().host_fn_weights);
|
||||
self.ext.gas_meter().adjust_gas(
|
||||
charged_amount,
|
||||
&self.ext.schedule().host_fn_weights,
|
||||
adjusted_amount,
|
||||
);
|
||||
}
|
||||
@@ -474,11 +487,11 @@ where
|
||||
pub fn read_sandbox_memory_as<D: Decode>(&mut self, ptr: u32, len: u32)
|
||||
-> Result<D, DispatchError>
|
||||
{
|
||||
let amount = self.charge_gas(RuntimeToken::CopyIn(len))?;
|
||||
let amount = self.charge_gas(RuntimeCosts::CopyIn(len))?;
|
||||
let buf = self.read_sandbox_memory(ptr, len)?;
|
||||
let decoded = D::decode_all(&mut &buf[..])
|
||||
.map_err(|_| DispatchError::from(Error::<E::T>::DecodingFailed))?;
|
||||
self.gas_meter.refund(amount);
|
||||
self.ext.gas_meter().refund(amount);
|
||||
Ok(decoded)
|
||||
}
|
||||
|
||||
@@ -507,7 +520,7 @@ where
|
||||
out_len_ptr: u32,
|
||||
buf: &[u8],
|
||||
allow_skip: bool,
|
||||
create_token: impl FnOnce(u32) -> Option<RuntimeToken>,
|
||||
create_token: impl FnOnce(u32) -> Option<RuntimeCosts>,
|
||||
) -> Result<(), DispatchError>
|
||||
{
|
||||
if allow_skip && out_ptr == u32::max_value() {
|
||||
@@ -521,8 +534,8 @@ where
|
||||
Err(Error::<E::T>::OutputBufferTooSmall)?
|
||||
}
|
||||
|
||||
if let Some(token) = create_token(buf_len) {
|
||||
self.charge_gas(token)?;
|
||||
if let Some(costs) = create_token(buf_len) {
|
||||
self.charge_gas(costs)?;
|
||||
}
|
||||
|
||||
self.memory.set(out_ptr, buf).and_then(|_| {
|
||||
@@ -631,7 +644,7 @@ define_env!(Env, <E: Ext>,
|
||||
//
|
||||
// - amount: How much gas is used.
|
||||
[seal0] gas(ctx, amount: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::MeteringBlock(amount))?;
|
||||
ctx.charge_gas(RuntimeCosts::MeteringBlock(amount))?;
|
||||
Ok(())
|
||||
},
|
||||
|
||||
@@ -651,7 +664,7 @@ define_env!(Env, <E: Ext>,
|
||||
// - If value length exceeds the configured maximum value length of a storage entry.
|
||||
// - Upon trying to set an empty storage entry (value length is 0).
|
||||
[seal0] seal_set_storage(ctx, key_ptr: u32, value_ptr: u32, value_len: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::SetStorage(value_len))?;
|
||||
ctx.charge_gas(RuntimeCosts::SetStorage(value_len))?;
|
||||
if value_len > ctx.ext.max_value_size() {
|
||||
Err(Error::<E::T>::ValueTooLarge)?;
|
||||
}
|
||||
@@ -667,7 +680,7 @@ define_env!(Env, <E: Ext>,
|
||||
//
|
||||
// - `key_ptr`: pointer into the linear memory where the location to clear the value is placed.
|
||||
[seal0] seal_clear_storage(ctx, key_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::ClearStorage)?;
|
||||
ctx.charge_gas(RuntimeCosts::ClearStorage)?;
|
||||
let mut key: StorageKey = [0; 32];
|
||||
ctx.read_sandbox_memory_into_buf(key_ptr, &mut key)?;
|
||||
ctx.ext.set_storage(key, None).map_err(Into::into)
|
||||
@@ -686,12 +699,12 @@ define_env!(Env, <E: Ext>,
|
||||
//
|
||||
// `ReturnCode::KeyNotFound`
|
||||
[seal0] seal_get_storage(ctx, key_ptr: u32, out_ptr: u32, out_len_ptr: u32) -> ReturnCode => {
|
||||
ctx.charge_gas(RuntimeToken::GetStorageBase)?;
|
||||
ctx.charge_gas(RuntimeCosts::GetStorageBase)?;
|
||||
let mut key: StorageKey = [0; 32];
|
||||
ctx.read_sandbox_memory_into_buf(key_ptr, &mut key)?;
|
||||
if let Some(value) = ctx.ext.get_storage(&key) {
|
||||
ctx.write_sandbox_output(out_ptr, out_len_ptr, &value, false, |len| {
|
||||
Some(RuntimeToken::GetStorageCopyOut(len))
|
||||
Some(RuntimeCosts::GetStorageCopyOut(len))
|
||||
})?;
|
||||
Ok(ReturnCode::Success)
|
||||
} else {
|
||||
@@ -721,7 +734,7 @@ define_env!(Env, <E: Ext>,
|
||||
value_ptr: u32,
|
||||
value_len: u32
|
||||
) -> ReturnCode => {
|
||||
ctx.charge_gas(RuntimeToken::Transfer)?;
|
||||
ctx.charge_gas(RuntimeCosts::Transfer)?;
|
||||
let callee: <<E as Ext>::T as frame_system::Config>::AccountId =
|
||||
ctx.read_sandbox_memory_as(account_ptr, account_len)?;
|
||||
let value: BalanceOf<<E as Ext>::T> =
|
||||
@@ -780,45 +793,27 @@ define_env!(Env, <E: Ext>,
|
||||
output_ptr: u32,
|
||||
output_len_ptr: u32
|
||||
) -> ReturnCode => {
|
||||
ctx.charge_gas(RuntimeToken::CallBase(input_data_len))?;
|
||||
ctx.charge_gas(RuntimeCosts::CallBase(input_data_len))?;
|
||||
let callee: <<E as Ext>::T as frame_system::Config>::AccountId =
|
||||
ctx.read_sandbox_memory_as(callee_ptr, callee_len)?;
|
||||
let value: BalanceOf<<E as Ext>::T> = ctx.read_sandbox_memory_as(value_ptr, value_len)?;
|
||||
let input_data = ctx.read_sandbox_memory(input_data_ptr, input_data_len)?;
|
||||
if value > 0u32.into() {
|
||||
ctx.charge_gas(RuntimeToken::CallSurchargeTransfer)?;
|
||||
ctx.charge_gas(RuntimeCosts::CallSurchargeTransfer)?;
|
||||
}
|
||||
let charged = ctx.charge_gas(
|
||||
RuntimeToken::CallSurchargeCodeSize(<E::T as Config>::MaxCodeSize::get())
|
||||
RuntimeCosts::CallSurchargeCodeSize(<E::T as Config>::MaxCodeSize::get())
|
||||
)?;
|
||||
let nested_gas_limit = if gas == 0 {
|
||||
ctx.gas_meter.gas_left()
|
||||
} else {
|
||||
gas.saturated_into()
|
||||
};
|
||||
let ext = &mut ctx.ext;
|
||||
let call_outcome = ctx.gas_meter.with_nested(nested_gas_limit, |nested_meter| {
|
||||
match nested_meter {
|
||||
Some(nested_meter) => {
|
||||
ext.call(
|
||||
&callee,
|
||||
value,
|
||||
nested_meter,
|
||||
input_data,
|
||||
)
|
||||
}
|
||||
// there is not enough gas to allocate for the nested call.
|
||||
None => Err((Error::<<E as Ext>::T>::OutOfGas.into(), 0)),
|
||||
}
|
||||
});
|
||||
let call_outcome = ext.call(gas, callee, value, input_data);
|
||||
let code_len = match &call_outcome {
|
||||
Ok((_, len)) => len,
|
||||
Err((_, len)) => len,
|
||||
};
|
||||
ctx.adjust_gas(charged, RuntimeToken::CallSurchargeCodeSize(*code_len));
|
||||
ctx.adjust_gas(charged, RuntimeCosts::CallSurchargeCodeSize(*code_len));
|
||||
if let Ok((output, _)) = &call_outcome {
|
||||
ctx.write_sandbox_output(output_ptr, output_len_ptr, &output.data, true, |len| {
|
||||
Some(RuntimeToken::CallCopyOut(len))
|
||||
Some(RuntimeCosts::CallCopyOut(len))
|
||||
})?;
|
||||
}
|
||||
Ok(Runtime::<E>::exec_into_return_code(call_outcome.map(|r| r.0).map_err(|r| r.0))?)
|
||||
@@ -885,41 +880,22 @@ define_env!(Env, <E: Ext>,
|
||||
salt_ptr: u32,
|
||||
salt_len: u32
|
||||
) -> ReturnCode => {
|
||||
ctx.charge_gas(RuntimeToken::InstantiateBase {input_data_len, salt_len})?;
|
||||
ctx.charge_gas(RuntimeCosts::InstantiateBase {input_data_len, salt_len})?;
|
||||
let code_hash: CodeHash<<E as Ext>::T> =
|
||||
ctx.read_sandbox_memory_as(code_hash_ptr, code_hash_len)?;
|
||||
let value: BalanceOf<<E as Ext>::T> = ctx.read_sandbox_memory_as(value_ptr, value_len)?;
|
||||
let input_data = ctx.read_sandbox_memory(input_data_ptr, input_data_len)?;
|
||||
let salt = ctx.read_sandbox_memory(salt_ptr, salt_len)?;
|
||||
let charged = ctx.charge_gas(
|
||||
RuntimeToken::InstantiateSurchargeCodeSize(<E::T as Config>::MaxCodeSize::get())
|
||||
RuntimeCosts::InstantiateSurchargeCodeSize(<E::T as Config>::MaxCodeSize::get())
|
||||
)?;
|
||||
let nested_gas_limit = if gas == 0 {
|
||||
ctx.gas_meter.gas_left()
|
||||
} else {
|
||||
gas.saturated_into()
|
||||
};
|
||||
let ext = &mut ctx.ext;
|
||||
let instantiate_outcome = ctx.gas_meter.with_nested(nested_gas_limit, |nested_meter| {
|
||||
match nested_meter {
|
||||
Some(nested_meter) => {
|
||||
ext.instantiate(
|
||||
code_hash,
|
||||
value,
|
||||
nested_meter,
|
||||
input_data,
|
||||
&salt,
|
||||
)
|
||||
}
|
||||
// there is not enough gas to allocate for the nested call.
|
||||
None => Err((Error::<<E as Ext>::T>::OutOfGas.into(), 0)),
|
||||
}
|
||||
});
|
||||
let instantiate_outcome = ext.instantiate(gas, code_hash, value, input_data, &salt);
|
||||
let code_len = match &instantiate_outcome {
|
||||
Ok((_, _, code_len)) => code_len,
|
||||
Err((_, code_len)) => code_len,
|
||||
};
|
||||
ctx.adjust_gas(charged, RuntimeToken::InstantiateSurchargeCodeSize(*code_len));
|
||||
ctx.adjust_gas(charged, RuntimeCosts::InstantiateSurchargeCodeSize(*code_len));
|
||||
if let Ok((address, output, _)) = &instantiate_outcome {
|
||||
if !output.flags.contains(ReturnFlags::REVERT) {
|
||||
ctx.write_sandbox_output(
|
||||
@@ -927,7 +903,7 @@ define_env!(Env, <E: Ext>,
|
||||
)?;
|
||||
}
|
||||
ctx.write_sandbox_output(output_ptr, output_len_ptr, &output.data, true, |len| {
|
||||
Some(RuntimeToken::InstantiateCopyOut(len))
|
||||
Some(RuntimeCosts::InstantiateCopyOut(len))
|
||||
})?;
|
||||
}
|
||||
Ok(Runtime::<E>::exec_into_return_code(
|
||||
@@ -956,18 +932,18 @@ define_env!(Env, <E: Ext>,
|
||||
beneficiary_ptr: u32,
|
||||
beneficiary_len: u32
|
||||
) => {
|
||||
ctx.charge_gas(RuntimeToken::Terminate)?;
|
||||
ctx.charge_gas(RuntimeCosts::Terminate)?;
|
||||
let beneficiary: <<E as Ext>::T as frame_system::Config>::AccountId =
|
||||
ctx.read_sandbox_memory_as(beneficiary_ptr, beneficiary_len)?;
|
||||
|
||||
let charged = ctx.charge_gas(
|
||||
RuntimeToken::TerminateSurchargeCodeSize(<E::T as Config>::MaxCodeSize::get())
|
||||
RuntimeCosts::TerminateSurchargeCodeSize(<E::T as Config>::MaxCodeSize::get())
|
||||
)?;
|
||||
let (result, code_len) = match ctx.ext.terminate(&beneficiary) {
|
||||
Ok(len) => (Ok(()), len),
|
||||
Err((err, len)) => (Err(err), len),
|
||||
};
|
||||
ctx.adjust_gas(charged, RuntimeToken::TerminateSurchargeCodeSize(code_len));
|
||||
ctx.adjust_gas(charged, RuntimeCosts::TerminateSurchargeCodeSize(code_len));
|
||||
result?;
|
||||
Err(TrapReason::Termination)
|
||||
},
|
||||
@@ -983,10 +959,10 @@ define_env!(Env, <E: Ext>,
|
||||
//
|
||||
// This function can only be called once. Calling it multiple times will trigger a trap.
|
||||
[seal0] seal_input(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::InputBase)?;
|
||||
ctx.charge_gas(RuntimeCosts::InputBase)?;
|
||||
if let Some(input) = ctx.input_data.take() {
|
||||
ctx.write_sandbox_output(out_ptr, out_len_ptr, &input, false, |len| {
|
||||
Some(RuntimeToken::InputCopyOut(len))
|
||||
Some(RuntimeCosts::InputCopyOut(len))
|
||||
})?;
|
||||
Ok(())
|
||||
} else {
|
||||
@@ -1012,7 +988,7 @@ define_env!(Env, <E: Ext>,
|
||||
//
|
||||
// Using a reserved bit triggers a trap.
|
||||
[seal0] seal_return(ctx, flags: u32, data_ptr: u32, data_len: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::Return(data_len))?;
|
||||
ctx.charge_gas(RuntimeCosts::Return(data_len))?;
|
||||
Err(TrapReason::Return(ReturnData {
|
||||
flags,
|
||||
data: ctx.read_sandbox_memory(data_ptr, data_len)?,
|
||||
@@ -1030,7 +1006,7 @@ define_env!(Env, <E: Ext>,
|
||||
// extrinsic will be returned. Otherwise, if this call is initiated by another contract then the
|
||||
// address of the contract will be returned. The value is encoded as T::AccountId.
|
||||
[seal0] seal_caller(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::Caller)?;
|
||||
ctx.charge_gas(RuntimeCosts::Caller)?;
|
||||
Ok(ctx.write_sandbox_output(
|
||||
out_ptr, out_len_ptr, &ctx.ext.caller().encode(), false, already_charged
|
||||
)?)
|
||||
@@ -1043,7 +1019,7 @@ define_env!(Env, <E: Ext>,
|
||||
// `out_ptr`. This call overwrites it with the size of the value. If the available
|
||||
// space at `out_ptr` is less than the size of the value a trap is triggered.
|
||||
[seal0] seal_address(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::Address)?;
|
||||
ctx.charge_gas(RuntimeCosts::Address)?;
|
||||
Ok(ctx.write_sandbox_output(
|
||||
out_ptr, out_len_ptr, &ctx.ext.address().encode(), false, already_charged
|
||||
)?)
|
||||
@@ -1063,7 +1039,7 @@ define_env!(Env, <E: Ext>,
|
||||
// It is recommended to avoid specifying very small values for `gas` as the prices for a single
|
||||
// gas can be smaller than one.
|
||||
[seal0] seal_weight_to_fee(ctx, gas: u64, out_ptr: u32, out_len_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::WeightToFee)?;
|
||||
ctx.charge_gas(RuntimeCosts::WeightToFee)?;
|
||||
Ok(ctx.write_sandbox_output(
|
||||
out_ptr, out_len_ptr, &ctx.ext.get_weight_price(gas).encode(), false, already_charged
|
||||
)?)
|
||||
@@ -1078,9 +1054,10 @@ define_env!(Env, <E: Ext>,
|
||||
//
|
||||
// The data is encoded as Gas.
|
||||
[seal0] seal_gas_left(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::GasLeft)?;
|
||||
ctx.charge_gas(RuntimeCosts::GasLeft)?;
|
||||
let gas_left = &ctx.ext.gas_meter().gas_left().encode();
|
||||
Ok(ctx.write_sandbox_output(
|
||||
out_ptr, out_len_ptr, &ctx.gas_meter.gas_left().encode(), false, already_charged
|
||||
out_ptr, out_len_ptr, &gas_left, false, already_charged,
|
||||
)?)
|
||||
},
|
||||
|
||||
@@ -1093,7 +1070,7 @@ define_env!(Env, <E: Ext>,
|
||||
//
|
||||
// The data is encoded as T::Balance.
|
||||
[seal0] seal_balance(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::Balance)?;
|
||||
ctx.charge_gas(RuntimeCosts::Balance)?;
|
||||
Ok(ctx.write_sandbox_output(
|
||||
out_ptr, out_len_ptr, &ctx.ext.balance().encode(), false, already_charged
|
||||
)?)
|
||||
@@ -1108,7 +1085,7 @@ define_env!(Env, <E: Ext>,
|
||||
//
|
||||
// The data is encoded as T::Balance.
|
||||
[seal0] seal_value_transferred(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::ValueTransferred)?;
|
||||
ctx.charge_gas(RuntimeCosts::ValueTransferred)?;
|
||||
Ok(ctx.write_sandbox_output(
|
||||
out_ptr, out_len_ptr, &ctx.ext.value_transferred().encode(), false, already_charged
|
||||
)?)
|
||||
@@ -1127,7 +1104,7 @@ define_env!(Env, <E: Ext>,
|
||||
//
|
||||
// This function is deprecated. Users should migrate to the version in the "seal1" module.
|
||||
[seal0] seal_random(ctx, subject_ptr: u32, subject_len: u32, out_ptr: u32, out_len_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::Random)?;
|
||||
ctx.charge_gas(RuntimeCosts::Random)?;
|
||||
if subject_len > ctx.ext.schedule().limits.subject_len {
|
||||
Err(Error::<E::T>::RandomSubjectTooLong)?;
|
||||
}
|
||||
@@ -1159,7 +1136,7 @@ define_env!(Env, <E: Ext>,
|
||||
// call this on later blocks until the block number returned is later than the latest
|
||||
// commitment.
|
||||
[seal1] seal_random(ctx, subject_ptr: u32, subject_len: u32, out_ptr: u32, out_len_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::Random)?;
|
||||
ctx.charge_gas(RuntimeCosts::Random)?;
|
||||
if subject_len > ctx.ext.schedule().limits.subject_len {
|
||||
Err(Error::<E::T>::RandomSubjectTooLong)?;
|
||||
}
|
||||
@@ -1176,7 +1153,7 @@ define_env!(Env, <E: Ext>,
|
||||
// `out_ptr`. This call overwrites it with the size of the value. If the available
|
||||
// space at `out_ptr` is less than the size of the value a trap is triggered.
|
||||
[seal0] seal_now(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::Now)?;
|
||||
ctx.charge_gas(RuntimeCosts::Now)?;
|
||||
Ok(ctx.write_sandbox_output(
|
||||
out_ptr, out_len_ptr, &ctx.ext.now().encode(), false, already_charged
|
||||
)?)
|
||||
@@ -1186,7 +1163,7 @@ define_env!(Env, <E: Ext>,
|
||||
//
|
||||
// The data is encoded as T::Balance.
|
||||
[seal0] seal_minimum_balance(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::MinimumBalance)?;
|
||||
ctx.charge_gas(RuntimeCosts::MinimumBalance)?;
|
||||
Ok(ctx.write_sandbox_output(
|
||||
out_ptr, out_len_ptr, &ctx.ext.minimum_balance().encode(), false, already_charged
|
||||
)?)
|
||||
@@ -1208,7 +1185,7 @@ define_env!(Env, <E: Ext>,
|
||||
// below the sum of existential deposit and the tombstone deposit. The sum
|
||||
// is commonly referred as subsistence threshold in code.
|
||||
[seal0] seal_tombstone_deposit(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::TombstoneDeposit)?;
|
||||
ctx.charge_gas(RuntimeCosts::TombstoneDeposit)?;
|
||||
Ok(ctx.write_sandbox_output(
|
||||
out_ptr, out_len_ptr, &ctx.ext.tombstone_deposit().encode(), false, already_charged
|
||||
)?)
|
||||
@@ -1256,7 +1233,7 @@ define_env!(Env, <E: Ext>,
|
||||
delta_ptr: u32,
|
||||
delta_count: u32
|
||||
) => {
|
||||
ctx.charge_gas(RuntimeToken::RestoreTo(delta_count))?;
|
||||
ctx.charge_gas(RuntimeCosts::RestoreTo(delta_count))?;
|
||||
let dest: <<E as Ext>::T as frame_system::Config>::AccountId =
|
||||
ctx.read_sandbox_memory_as(dest_ptr, dest_len)?;
|
||||
let code_hash: CodeHash<<E as Ext>::T> =
|
||||
@@ -1290,7 +1267,7 @@ define_env!(Env, <E: Ext>,
|
||||
};
|
||||
|
||||
let max_len = <E::T as Config>::MaxCodeSize::get();
|
||||
let charged = ctx.charge_gas(RuntimeToken::RestoreToSurchargeCodeSize {
|
||||
let charged = ctx.charge_gas(RuntimeCosts::RestoreToSurchargeCodeSize {
|
||||
caller_code: max_len,
|
||||
tombstone_code: max_len,
|
||||
})?;
|
||||
@@ -1300,7 +1277,7 @@ define_env!(Env, <E: Ext>,
|
||||
Ok((code, tomb)) => (Ok(()), code, tomb),
|
||||
Err((err, code, tomb)) => (Err(err), code, tomb),
|
||||
};
|
||||
ctx.adjust_gas(charged, RuntimeToken::RestoreToSurchargeCodeSize {
|
||||
ctx.adjust_gas(charged, RuntimeCosts::RestoreToSurchargeCodeSize {
|
||||
caller_code,
|
||||
tombstone_code,
|
||||
});
|
||||
@@ -1341,7 +1318,7 @@ define_env!(Env, <E: Ext>,
|
||||
let num_topic = topics_len
|
||||
.checked_div(sp_std::mem::size_of::<TopicOf<E::T>>() as u32)
|
||||
.ok_or_else(|| "Zero sized topics are not allowed")?;
|
||||
ctx.charge_gas(RuntimeToken::DepositEvent {
|
||||
ctx.charge_gas(RuntimeCosts::DepositEvent {
|
||||
num_topic,
|
||||
len: data_len,
|
||||
})?;
|
||||
@@ -1379,7 +1356,7 @@ define_env!(Env, <E: Ext>,
|
||||
// Should be decodable as a `T::Balance`. Traps otherwise.
|
||||
// - value_len: length of the value buffer.
|
||||
[seal0] seal_set_rent_allowance(ctx, value_ptr: u32, value_len: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::SetRentAllowance)?;
|
||||
ctx.charge_gas(RuntimeCosts::SetRentAllowance)?;
|
||||
let value: BalanceOf<<E as Ext>::T> =
|
||||
ctx.read_sandbox_memory_as(value_ptr, value_len)?;
|
||||
ctx.ext.set_rent_allowance(value);
|
||||
@@ -1396,9 +1373,10 @@ define_env!(Env, <E: Ext>,
|
||||
//
|
||||
// The data is encoded as T::Balance.
|
||||
[seal0] seal_rent_allowance(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::RentAllowance)?;
|
||||
ctx.charge_gas(RuntimeCosts::RentAllowance)?;
|
||||
let rent_allowance = ctx.ext.rent_allowance().encode();
|
||||
Ok(ctx.write_sandbox_output(
|
||||
out_ptr, out_len_ptr, &ctx.ext.rent_allowance().encode(), false, already_charged
|
||||
out_ptr, out_len_ptr, &rent_allowance, false, already_charged
|
||||
)?)
|
||||
},
|
||||
|
||||
@@ -1420,7 +1398,7 @@ define_env!(Env, <E: Ext>,
|
||||
// `out_ptr`. This call overwrites it with the size of the value. If the available
|
||||
// space at `out_ptr` is less than the size of the value a trap is triggered.
|
||||
[seal0] seal_block_number(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::BlockNumber)?;
|
||||
ctx.charge_gas(RuntimeCosts::BlockNumber)?;
|
||||
Ok(ctx.write_sandbox_output(
|
||||
out_ptr, out_len_ptr, &ctx.ext.block_number().encode(), false, already_charged
|
||||
)?)
|
||||
@@ -1447,7 +1425,7 @@ define_env!(Env, <E: Ext>,
|
||||
// data is placed. The function will write the result
|
||||
// directly into this buffer.
|
||||
[seal0] seal_hash_sha2_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::HashSha256(input_len))?;
|
||||
ctx.charge_gas(RuntimeCosts::HashSha256(input_len))?;
|
||||
Ok(ctx.compute_hash_on_intermediate_buffer(sha2_256, input_ptr, input_len, output_ptr)?)
|
||||
},
|
||||
|
||||
@@ -1472,7 +1450,7 @@ define_env!(Env, <E: Ext>,
|
||||
// data is placed. The function will write the result
|
||||
// directly into this buffer.
|
||||
[seal0] seal_hash_keccak_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::HashKeccak256(input_len))?;
|
||||
ctx.charge_gas(RuntimeCosts::HashKeccak256(input_len))?;
|
||||
Ok(ctx.compute_hash_on_intermediate_buffer(keccak_256, input_ptr, input_len, output_ptr)?)
|
||||
},
|
||||
|
||||
@@ -1497,7 +1475,7 @@ define_env!(Env, <E: Ext>,
|
||||
// data is placed. The function will write the result
|
||||
// directly into this buffer.
|
||||
[seal0] seal_hash_blake2_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::HashBlake256(input_len))?;
|
||||
ctx.charge_gas(RuntimeCosts::HashBlake256(input_len))?;
|
||||
Ok(ctx.compute_hash_on_intermediate_buffer(blake2_256, input_ptr, input_len, output_ptr)?)
|
||||
},
|
||||
|
||||
@@ -1522,7 +1500,7 @@ define_env!(Env, <E: Ext>,
|
||||
// data is placed. The function will write the result
|
||||
// directly into this buffer.
|
||||
[seal0] seal_hash_blake2_128(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::HashBlake128(input_len))?;
|
||||
ctx.charge_gas(RuntimeCosts::HashBlake128(input_len))?;
|
||||
Ok(ctx.compute_hash_on_intermediate_buffer(blake2_128, input_ptr, input_len, output_ptr)?)
|
||||
},
|
||||
|
||||
@@ -1574,7 +1552,7 @@ define_env!(Env, <E: Ext>,
|
||||
// started execution. Any change to those values that happens due to actions of the
|
||||
// current call or contracts that are called by this contract are not considered.
|
||||
[seal0] seal_rent_params(ctx, out_ptr: u32, out_len_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeToken::RentParams)?;
|
||||
ctx.charge_gas(RuntimeCosts::RentParams)?;
|
||||
Ok(ctx.write_sandbox_output(
|
||||
out_ptr, out_len_ptr, &ctx.ext.rent_params().encode(), false, already_charged
|
||||
)?)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user