Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

contracts: Charge rent for code storage #7935

Merged
20 commits merged into from
Feb 4, 2021
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 4 additions & 10 deletions bin/node/executor/tests/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -602,23 +602,17 @@ fn deploying_wasm_contract_should_work() {
CheckedExtrinsic {
signed: Some((charlie(), signed_extra(0, 0))),
function: Call::Contracts(
pallet_contracts::Call::put_code::<Runtime>(transfer_code)
),
},
CheckedExtrinsic {
signed: Some((charlie(), signed_extra(1, 0))),
function: Call::Contracts(
pallet_contracts::Call::instantiate::<Runtime>(
1 * DOLLARS + subsistence,
pallet_contracts::Call::instantiate_with_code::<Runtime>(
1000 * DOLLARS + subsistence,
500_000_000,
transfer_ch,
transfer_code,
Vec::new(),
Vec::new(),
)
),
},
CheckedExtrinsic {
signed: Some((charlie(), signed_extra(2, 0))),
signed: Some((charlie(), signed_extra(1, 0))),
function: Call::Contracts(
pallet_contracts::Call::call::<Runtime>(
sp_runtime::MultiAddress::Id(addr.clone()),
Expand Down
10 changes: 3 additions & 7 deletions frame/contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,23 +42,19 @@ fails, A can decide how to handle that failure, either proceeding or reverting A

### 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.
* `call` - Makes a call to an account, optionally transferring some balance.
Those are documented in the reference documentation of the `Module`.

## Usage

The Contract module is a work in progress. The following examples show how this Contract module
can be used to instantiate and call contracts.

* [`ink`](https://github.com/paritytech/ink) is
- [`ink`](https://github.com/paritytech/ink) is
an [`eDSL`](https://wiki.haskell.org/Embedded_domain_specific_language) that enables writing
WebAssembly based smart contracts in the Rust programming language. This is a work in progress.

## Related Modules

* [Balances](https://docs.rs/pallet-balances/latest/pallet_balances/)
- [Balances](https://docs.rs/pallet-balances/latest/pallet_balances/)

License: Apache-2.0
4 changes: 2 additions & 2 deletions frame/contracts/fixtures/instantiate_return_code.wat
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
(import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
(import "env" "memory" (memory 1 1))

;; [0, 8) 100 balance
(data (i32.const 0) "\64\00\00\00\00\00\00\00")
;; [0, 8) 10_000 balance
(data (i32.const 0) "\10\27\00\00\00\00\00\00")

;; [8, 12) here we store the return code of the transfer

Expand Down
11 changes: 6 additions & 5 deletions frame/contracts/src/benchmarking/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ pub struct ImportedFunction {
pub return_type: Option<ValueType>,
}

/// A wasm module ready to be put on chain with `put_code`.
/// A wasm module ready to be put on chain.
#[derive(Clone)]
pub struct WasmModule<T:Config> {
pub code: Vec<u8>,
Expand Down Expand Up @@ -245,16 +245,16 @@ where
}

/// Creates a wasm module of `target_bytes` size. Used to benchmark the performance of
/// `put_code` for different sizes of wasm modules. The generated module maximizes
/// `instantiate_with_code` for different sizes of wasm modules. The generated module maximizes
/// instrumentation runtime by nesting blocks as deeply as possible given the byte budget.
pub fn sized(target_bytes: u32) -> Self {
use parity_wasm::elements::Instruction::{If, I32Const, Return, End};
// Base size of a contract is 47 bytes and each expansion adds 6 bytes.
// Base size of a contract is 63 bytes and each expansion adds 6 bytes.
// We do one expansion less to account for the code section and function body
// size fields inside the binary wasm module representation which are leb128 encoded
// and therefore grow in size when the contract grows. We are not allowed to overshoot
// because of the maximum code size that is enforced by `put_code`.
let expansions = (target_bytes.saturating_sub(47) / 6).saturating_sub(1);
// because of the maximum code size that is enforced by `instantiate_with_code`.
let expansions = (target_bytes.saturating_sub(63) / 6).saturating_sub(1);
const EXPANSION: [Instruction; 4] = [
I32Const(0),
If(BlockType::NoResult),
Expand All @@ -263,6 +263,7 @@ where
];
ModuleDefinition {
call_body: Some(body::repeated(expansions, &EXPANSION)),
memory: Some(ImportedMemory::max::<T>()),
.. Default::default()
}
.into()
Expand Down
48 changes: 29 additions & 19 deletions frame/contracts/src/benchmarking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ where
// same block number.
System::<T>::set_block_number(1u32.into());

Contracts::<T>::put_code_raw(module.code)?;
Contracts::<T>::store_code_raw(module.code)?;
Contracts::<T>::instantiate(
RawOrigin::Signed(caller.clone()).into(),
endowment,
Expand Down Expand Up @@ -198,7 +198,7 @@ where
/// Get the block number when this contract will be evicted. Returns an error when
/// the rent collection won't happen because the contract has to much endowment.
fn eviction_at(&self) -> Result<T::BlockNumber, &'static str> {
let projection = Rent::<T>::compute_projection(&self.account_id)
let projection = Rent::<T, PrefabWasmModule<T>>::compute_projection(&self.account_id)
.map_err(|_| "Invalid acc for rent")?;
match projection {
RentProjection::EvictionAt(at) => Ok(at),
Expand Down Expand Up @@ -250,7 +250,7 @@ where
/// Evict this contract.
fn evict(&mut self) -> Result<(), &'static str> {
self.set_block_num_for_eviction()?;
Rent::<T>::try_eviction(&self.contract.account_id, Zero::zero())?;
Rent::<T, PrefabWasmModule<T>>::try_eviction(&self.contract.account_id, Zero::zero())?;
self.contract.ensure_tombstone()
}
}
Expand Down Expand Up @@ -314,33 +314,43 @@ benchmarks! {

// This constructs a contract that is maximal expensive to instrument.
// It creates a maximum number of metering blocks per byte.
// `n`: Size of the code in kilobytes.
put_code {
let n in 0 .. Contracts::<T>::current_schedule().limits.code_size / 1024;
// The size of the salt influences the runtime because is is hashed in order to
// determine the contract address.
// `c`: Size of the code in kilobytes.
// `s`: Size of the salt in kilobytes.
instantiate_with_code {
let c in 0 .. Contracts::<T>::current_schedule().limits.code_size / 1024;
let s in 0 .. code::max_pages::<T>() * 64;
let salt = vec![42u8; (s * 1024) as usize];
let endowment = caller_funding::<T>() / 3u32.into();
let caller = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, caller_funding::<T>());
let module = WasmModule::<T>::sized(n * 1024);
let origin = RawOrigin::Signed(caller);
}: _(origin, module.code)
let WasmModule { code, hash, .. } = WasmModule::<T>::sized(c * 1024);
let origin = RawOrigin::Signed(caller.clone());
let addr = Contracts::<T>::contract_address(&caller, &hash, &salt);
}: _(origin, endowment, Weight::max_value(), code, vec![], salt)
verify {
// endowment was removed from the caller
assert_eq!(T::Currency::free_balance(&caller), caller_funding::<T>() - endowment);
// contract has the full endowment because no rent collection happended
assert_eq!(T::Currency::free_balance(&addr), endowment);
// instantiate should leave a alive contract
Contract::<T>::address_alive_info(&addr)?;
}

// Instantiate uses a dummy contract constructor to measure the overhead of the instantiate.
// The size of the input data influences the runtime because it is hashed in order to determine
// the contract address.
// `n`: Size of the data passed to constructor in kilobytes.
athei marked this conversation as resolved.
Show resolved Hide resolved
// `s`: Size of the salt in kilobytes.
instantiate {
let n in 0 .. code::max_pages::<T>() * 64;
let s in 0 .. code::max_pages::<T>() * 64;
let data = vec![42u8; (n * 1024) as usize];
let salt = vec![42u8; (s * 1024) as usize];
let endowment = caller_funding::<T>() / 3u32.into();
let caller = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, caller_funding::<T>());
let WasmModule { code, hash, .. } = WasmModule::<T>::dummy_with_mem();
let origin = RawOrigin::Signed(caller.clone());
let addr = Contracts::<T>::contract_address(&caller, &hash, &salt);
Contracts::<T>::put_code_raw(code)?;
}: _(origin, endowment, Weight::max_value(), hash, data, salt)
Contracts::<T>::store_code_raw(code)?;
}: _(origin, endowment, Weight::max_value(), hash, vec![], salt)
verify {
// endowment was removed from the caller
assert_eq!(T::Currency::free_balance(&caller), caller_funding::<T>() - endowment);
Expand Down Expand Up @@ -1369,7 +1379,7 @@ benchmarks! {
])),
.. Default::default()
});
Contracts::<T>::put_code_raw(code.code)?;
Contracts::<T>::store_code_raw(code.code)?;
Ok(code.hash)
})
.collect::<Result<Vec<_>, &'static str>>()?;
Expand Down Expand Up @@ -1492,7 +1502,7 @@ benchmarks! {
let hash = callee_code.hash.clone();
let hash_bytes = callee_code.hash.encode();
let hash_len = hash_bytes.len();
Contracts::<T>::put_code_raw(callee_code.code)?;
Contracts::<T>::store_code_raw(callee_code.code)?;
let inputs = (0..API_BENCHMARK_BATCH_SIZE).map(|x| x.encode()).collect::<Vec<_>>();
let input_len = inputs.get(0).map(|x| x.len()).unwrap_or(0);
let input_bytes = inputs.iter().cloned().flatten().collect::<Vec<_>>();
Expand Down Expand Up @@ -2455,7 +2465,7 @@ mod tests {
create_test!(on_initialize_per_queue_item);

create_test!(update_schedule);
create_test!(put_code);
create_test!(instantiate_with_code);
create_test!(instantiate);
create_test!(call);
create_test!(claim_surcharge);
Expand Down
Loading