mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-29 12:37:57 +00:00
contracts: Charge rent for code storage (#7935)
* contracts: Implement refcounting for wasm code * contracts: Charge rent for code storage * contracts: Fix dispatchables erroneously refunding base costs * Fixed typos in comments. Co-authored-by: Andrew Jones <ascjones@gmail.com> * Remove awkward empty line * Fix more typos in docs * Fix typos in docs Co-authored-by: Andrew Jones <ascjones@gmail.com> * Split up complicated expression Co-authored-by: Andrew Jones <ascjones@gmail.com> * review: Remove unused return value * Fix typos Co-authored-by: Andrew Jones <ascjones@gmail.com> * review: Fix refcount being reset to one on re-instrumentation * Document evictable_code parameter * Make Executable::execute consume and store itself * Added comments about stale values * Disregard struct size in occupied_storage() Co-authored-by: Andrew Jones <ascjones@gmail.com>
This commit is contained in:
committed by
GitHub
parent
5569313bd6
commit
8e49a8a6a6
@@ -59,10 +59,11 @@
|
||||
//!
|
||||
//! ### Dispatchable functions
|
||||
//!
|
||||
//! * `put_code` - Stores the given binary Wasm code into the chain's storage and returns its `code_hash`.
|
||||
//! * `instantiate` - Deploys a new contract from the given `code_hash`, optionally transferring some balance.
|
||||
//! This instantiates a new smart contract account and calls its contract deploy handler to
|
||||
//! initialize the contract.
|
||||
//! * `instantiate_with_code` - Deploys a new contract from the supplied wasm binary, optionally transferring
|
||||
//! some balance. This instantiates a new smart contract account and calls its contract deploy
|
||||
//! handler to initialize the contract.
|
||||
//! * `instantiate` - The same as `instantiate_with_code` but instead of uploading new code an
|
||||
//! existing `code_hash` is supplied.
|
||||
//! * `call` - Makes a call to an account, optionally transferring some balance.
|
||||
//!
|
||||
//! ## Usage
|
||||
@@ -98,13 +99,12 @@ mod tests;
|
||||
|
||||
pub use crate::{
|
||||
gas::{Gas, GasMeter},
|
||||
wasm::ReturnCode as RuntimeReturnCode,
|
||||
wasm::{ReturnCode as RuntimeReturnCode, PrefabWasmModule},
|
||||
weights::WeightInfo,
|
||||
schedule::{Schedule, HostFnWeights, InstructionWeights, Limits},
|
||||
};
|
||||
use crate::{
|
||||
exec::ExecutionContext,
|
||||
wasm::{WasmLoader, WasmVm},
|
||||
exec::{ExecutionContext, Executable},
|
||||
rent::Rent,
|
||||
storage::Storage,
|
||||
};
|
||||
@@ -387,7 +387,8 @@ decl_error! {
|
||||
/// The contract that was called is either no contract at all (a plain account)
|
||||
/// or is a tombstone.
|
||||
NotCallable,
|
||||
/// The code supplied to `put_code` exceeds the limit specified in the current schedule.
|
||||
/// The code supplied to `instantiate_with_code` exceeds the limit specified in the
|
||||
/// current schedule.
|
||||
CodeTooLarge,
|
||||
/// No code could be found at the supplied code hash.
|
||||
CodeNotFound,
|
||||
@@ -431,6 +432,8 @@ decl_error! {
|
||||
/// This can either happen when the accumulated storage in bytes is too large or
|
||||
/// when number of storage items is too large.
|
||||
StorageExhausted,
|
||||
/// A contract with the same AccountId already exists.
|
||||
DuplicateContract,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,23 +531,6 @@ decl_module! {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Stores the given binary Wasm code into the chain's storage and returns its `codehash`.
|
||||
/// You can instantiate contracts only with stored code.
|
||||
#[weight = T::WeightInfo::put_code(code.len() as u32 / 1024)]
|
||||
pub fn put_code(
|
||||
origin,
|
||||
code: Vec<u8>
|
||||
) -> DispatchResult {
|
||||
ensure_signed(origin)?;
|
||||
let schedule = <Module<T>>::current_schedule();
|
||||
ensure!(code.len() as u32 <= schedule.limits.code_size, Error::<T>::CodeTooLarge);
|
||||
let result = wasm::save_code::<T>(code, &schedule);
|
||||
if let Ok(code_hash) = result {
|
||||
Self::deposit_event(RawEvent::CodeStored(code_hash));
|
||||
}
|
||||
result.map(|_| ()).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Makes a call to an account, optionally transferring some balance.
|
||||
///
|
||||
/// * If the account is a smart-contract account, the associated code will be
|
||||
@@ -563,31 +549,73 @@ decl_module! {
|
||||
let origin = ensure_signed(origin)?;
|
||||
let dest = T::Lookup::lookup(dest)?;
|
||||
let mut gas_meter = GasMeter::new(gas_limit);
|
||||
|
||||
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)
|
||||
gas_meter.into_dispatch_result(result, T::WeightInfo::call())
|
||||
}
|
||||
|
||||
/// Instantiates a new contract from the `code_hash` generated by `put_code`,
|
||||
/// optionally transferring some balance.
|
||||
/// Instantiates a new contract from the supplied `code` optionally transferring
|
||||
/// some balance.
|
||||
///
|
||||
/// The supplied `salt` is used for contract address deriviation. See `fn contract_address`.
|
||||
/// This is the only function that can deploy new code to the chain.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `endowment`: The balance to transfer from the `origin` to the newly created contract.
|
||||
/// * `gas_limit`: The gas limit enforced when executing the constructor.
|
||||
/// * `code`: The contract code to deploy in raw bytes.
|
||||
/// * `data`: The input data to pass to the contract constructor.
|
||||
/// * `salt`: Used for the address derivation. See [`Self::contract_address`].
|
||||
///
|
||||
/// Instantiation is executed as follows:
|
||||
///
|
||||
/// - The supplied `code` is instrumented, deployed, and a `code_hash` is created for that code.
|
||||
/// - If the `code_hash` already exists on the chain the underlying `code` will be shared.
|
||||
/// - The destination address is computed based on the sender, code_hash and the salt.
|
||||
/// - The smart-contract account is created at the computed address.
|
||||
/// - The `ctor_code` is executed in the context of the newly-created account. Buffer returned
|
||||
/// 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.
|
||||
/// - The `endowment` is transferred to the new account.
|
||||
/// - The `deploy` function is executed in the context of the newly-created account.
|
||||
#[weight =
|
||||
T::WeightInfo::instantiate(
|
||||
data.len() as u32 / 1024,
|
||||
T::WeightInfo::instantiate_with_code(
|
||||
code.len() as u32 / 1024,
|
||||
salt.len() as u32 / 1024,
|
||||
).saturating_add(*gas_limit)
|
||||
)
|
||||
.saturating_add(*gas_limit)
|
||||
]
|
||||
pub fn instantiate_with_code(
|
||||
origin,
|
||||
#[compact] endowment: BalanceOf<T>,
|
||||
#[compact] gas_limit: Gas,
|
||||
code: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
salt: Vec<u8>,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
let origin = ensure_signed(origin)?;
|
||||
let schedule = <Module<T>>::current_schedule();
|
||||
let code_len = code.len() as u32;
|
||||
ensure!(code_len <= schedule.limits.code_size, Error::<T>::CodeTooLarge);
|
||||
let mut gas_meter = GasMeter::new(gas_limit);
|
||||
let result = Self::execute_wasm(origin, &mut gas_meter, |ctx, gas_meter| {
|
||||
let executable = PrefabWasmModule::from_code(code, &schedule)?;
|
||||
let result = ctx.instantiate(endowment, gas_meter, executable, data, &salt)
|
||||
.map(|(_address, output)| output)?;
|
||||
Ok(result)
|
||||
});
|
||||
gas_meter.into_dispatch_result(
|
||||
result,
|
||||
T::WeightInfo::instantiate_with_code(code_len / 1024, salt.len() as u32 / 1024)
|
||||
)
|
||||
}
|
||||
|
||||
/// Instantiates a contract from a previously deployed wasm binary.
|
||||
///
|
||||
/// This function is identical to [`Self::instantiate_with_code`] but without the
|
||||
/// code deployment step. Instead, the `code_hash` of an on-chain deployed wasm binary
|
||||
/// must be supplied.
|
||||
#[weight =
|
||||
T::WeightInfo::instantiate(salt.len() as u32 / 1024)
|
||||
.saturating_add(*gas_limit)
|
||||
]
|
||||
pub fn instantiate(
|
||||
origin,
|
||||
@@ -599,12 +627,16 @@ decl_module! {
|
||||
) -> DispatchResultWithPostInfo {
|
||||
let origin = ensure_signed(origin)?;
|
||||
let mut gas_meter = GasMeter::new(gas_limit);
|
||||
|
||||
let result = Self::execute_wasm(origin, &mut gas_meter, |ctx, gas_meter| {
|
||||
ctx.instantiate(endowment, gas_meter, &code_hash, data, &salt)
|
||||
.map(|(_address, output)| output)
|
||||
let executable = PrefabWasmModule::from_storage(code_hash, &ctx.config.schedule)?;
|
||||
let result = ctx.instantiate(endowment, gas_meter, executable, data, &salt)
|
||||
.map(|(_address, output)| output)?;
|
||||
Ok(result)
|
||||
});
|
||||
gas_meter.into_dispatch_result(result)
|
||||
gas_meter.into_dispatch_result(
|
||||
result,
|
||||
T::WeightInfo::instantiate(salt.len() as u32 / 1024)
|
||||
)
|
||||
}
|
||||
|
||||
/// Allows block producers to claim a small reward for evicting a contract. If a block
|
||||
@@ -643,7 +675,9 @@ decl_module! {
|
||||
};
|
||||
|
||||
// If poking the contract has lead to eviction of the contract, give out the rewards.
|
||||
if let Some(rent_payed) = Rent::<T>::try_eviction(&dest, handicap)? {
|
||||
if let Some(rent_payed) =
|
||||
Rent::<T, PrefabWasmModule<T>>::try_eviction(&dest, handicap)?
|
||||
{
|
||||
T::Currency::deposit_into_existing(
|
||||
&rewarded,
|
||||
T::SurchargeReward::get().min(rent_payed),
|
||||
@@ -698,15 +732,15 @@ where
|
||||
}
|
||||
|
||||
pub fn rent_projection(address: T::AccountId) -> RentProjectionResult<T::BlockNumber> {
|
||||
Rent::<T>::compute_projection(&address)
|
||||
Rent::<T, PrefabWasmModule<T>>::compute_projection(&address)
|
||||
}
|
||||
|
||||
/// Put code for benchmarks which does not check or instrument the code.
|
||||
/// Store code for benchmarks which does not check nor instrument the code.
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
pub fn put_code_raw(code: Vec<u8>) -> DispatchResult {
|
||||
pub fn store_code_raw(code: Vec<u8>) -> DispatchResult {
|
||||
let schedule = <Module<T>>::current_schedule();
|
||||
let result = wasm::save_code_raw::<T>(code, &schedule);
|
||||
result.map(|_| ()).map_err(Into::into)
|
||||
PrefabWasmModule::store_code_unchecked(code, &schedule)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Determine the address of a contract,
|
||||
@@ -739,12 +773,13 @@ where
|
||||
fn execute_wasm(
|
||||
origin: T::AccountId,
|
||||
gas_meter: &mut GasMeter<T>,
|
||||
func: impl FnOnce(&mut ExecutionContext<T, WasmVm<T>, WasmLoader<T>>, &mut GasMeter<T>) -> ExecResult,
|
||||
func: impl FnOnce(
|
||||
&mut ExecutionContext<T, PrefabWasmModule<T>>,
|
||||
&mut GasMeter<T>,
|
||||
) -> ExecResult,
|
||||
) -> ExecResult {
|
||||
let cfg = ConfigCache::preload();
|
||||
let vm = WasmVm::new(&cfg.schedule);
|
||||
let loader = WasmLoader::new(&cfg.schedule);
|
||||
let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader);
|
||||
let mut ctx = ExecutionContext::top_level(origin, &cfg);
|
||||
func(&mut ctx, gas_meter)
|
||||
}
|
||||
}
|
||||
@@ -807,6 +842,12 @@ decl_event! {
|
||||
/// - `data`: Data supplied by the contract. Metadata generated during contract
|
||||
/// compilation is needed to decode it.
|
||||
ContractEmitted(AccountId, Vec<u8>),
|
||||
|
||||
/// A code with the specified hash was removed.
|
||||
/// \[code_hash\]
|
||||
///
|
||||
/// This happens when the last contract that uses this code hash was removed or evicted.
|
||||
CodeRemoved(Hash),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -820,7 +861,7 @@ decl_storage! {
|
||||
/// A mapping from an original code hash to the original code, untouched by instrumentation.
|
||||
pub PristineCode: map hasher(identity) CodeHash<T> => Option<Vec<u8>>;
|
||||
/// A mapping between an original code hash and instrumented wasm code, ready for execution.
|
||||
pub CodeStorage: map hasher(identity) CodeHash<T> => Option<wasm::PrefabWasmModule>;
|
||||
pub CodeStorage: map hasher(identity) CodeHash<T> => Option<PrefabWasmModule<T>>;
|
||||
/// The subtrie counter.
|
||||
pub AccountCounter: u64 = 0;
|
||||
/// The code associated with a given account.
|
||||
|
||||
Reference in New Issue
Block a user