mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 21:31:04 +00:00
contracts: Reduce the API surface (#8359)
* contracts: Remove types and storage from the public interface * contracts: Remove current_schedule() getter * contracts: Improve documentation * Update README.md * Fix integration test
This commit is contained in:
committed by
GitHub
parent
d343683ca9
commit
d98a3c7942
@@ -295,10 +295,9 @@ pub fn testnet_genesis(
|
|||||||
phantom: Default::default(),
|
phantom: Default::default(),
|
||||||
},
|
},
|
||||||
pallet_contracts: ContractsConfig {
|
pallet_contracts: ContractsConfig {
|
||||||
current_schedule: pallet_contracts::Schedule {
|
// println should only be enabled on development chains
|
||||||
enable_println, // this should only be enabled on development chains
|
current_schedule: pallet_contracts::Schedule::default()
|
||||||
..Default::default()
|
.enable_println(enable_println),
|
||||||
},
|
|
||||||
},
|
},
|
||||||
pallet_sudo: SudoConfig {
|
pallet_sudo: SudoConfig {
|
||||||
key: root_key,
|
key: root_key,
|
||||||
|
|||||||
@@ -656,13 +656,10 @@ fn deploying_wasm_contract_should_work() {
|
|||||||
).0.unwrap();
|
).0.unwrap();
|
||||||
|
|
||||||
t.execute_with(|| {
|
t.execute_with(|| {
|
||||||
// Verify that the contract constructor worked well and code of TRANSFER contract is actually deployed.
|
// Verify that the contract does exist by querying some of its storage items
|
||||||
assert_eq!(
|
// It does not matter that the storage item itself does not exist.
|
||||||
&pallet_contracts::ContractInfoOf::<Runtime>::get(addr)
|
assert!(
|
||||||
.and_then(|c| c.get_alive())
|
&pallet_contracts::Pallet::<Runtime>::get_storage(addr, Default::default()).is_ok()
|
||||||
.unwrap()
|
|
||||||
.code_hash,
|
|
||||||
&transfer_ch
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -744,11 +744,11 @@ impl pallet_tips::Config for Runtime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
pub const TombstoneDeposit: Balance = deposit(
|
pub TombstoneDeposit: Balance = deposit(
|
||||||
1,
|
1,
|
||||||
sp_std::mem::size_of::<pallet_contracts::ContractInfo<Runtime>>() as u32
|
<pallet_contracts::Pallet<Runtime>>::contract_info_size(),
|
||||||
);
|
);
|
||||||
pub const DepositPerContract: Balance = TombstoneDeposit::get();
|
pub DepositPerContract: Balance = TombstoneDeposit::get();
|
||||||
pub const DepositPerStorageByte: Balance = deposit(0, 1);
|
pub const DepositPerStorageByte: Balance = deposit(0, 1);
|
||||||
pub const DepositPerStorageItem: Balance = deposit(1, 0);
|
pub const DepositPerStorageItem: Balance = deposit(1, 0);
|
||||||
pub RentFraction: Perbill = Perbill::from_rational(1u32, 30 * DAYS);
|
pub RentFraction: Perbill = Perbill::from_rational(1u32, 30 * DAYS);
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ In other words: Upgrading this pallet will not break pre-existing contracts.
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Make storage and fields of `Schedule` private to the crate.
|
||||||
|
[1](https://github.com/paritytech/substrate/pull/8359)
|
||||||
|
|
||||||
- Add new version of `seal_random` which exposes additional information.
|
- Add new version of `seal_random` which exposes additional information.
|
||||||
[1](https://github.com/paritytech/substrate/pull/8329)
|
[1](https://github.com/paritytech/substrate/pull/8329)
|
||||||
|
|
||||||
|
|||||||
@@ -24,9 +24,7 @@
|
|||||||
//! we define this simple definition of a contract that can be passed to `create_code` that
|
//! we define this simple definition of a contract that can be passed to `create_code` that
|
||||||
//! compiles it down into a `WasmModule` that can be used as a contract's code.
|
//! compiles it down into a `WasmModule` that can be used as a contract's code.
|
||||||
|
|
||||||
use crate::Config;
|
use crate::{Config, CurrentSchedule};
|
||||||
use crate::Pallet as Contracts;
|
|
||||||
|
|
||||||
use parity_wasm::elements::{
|
use parity_wasm::elements::{
|
||||||
Instruction, Instructions, FuncBody, ValueType, BlockType, Section, CustomSection,
|
Instruction, Instructions, FuncBody, ValueType, BlockType, Section, CustomSection,
|
||||||
};
|
};
|
||||||
@@ -225,7 +223,7 @@ where
|
|||||||
if def.inject_stack_metering {
|
if def.inject_stack_metering {
|
||||||
code = inject_limiter(
|
code = inject_limiter(
|
||||||
code,
|
code,
|
||||||
Contracts::<T>::current_schedule().limits.stack_height
|
<CurrentSchedule<T>>::get().limits.stack_height
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
@@ -505,5 +503,5 @@ where
|
|||||||
T: Config,
|
T: Config,
|
||||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
||||||
{
|
{
|
||||||
Contracts::<T>::current_schedule().limits.memory_pages
|
<CurrentSchedule<T>>::get().limits.memory_pages
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ use self::{
|
|||||||
},
|
},
|
||||||
sandbox::Sandbox,
|
sandbox::Sandbox,
|
||||||
};
|
};
|
||||||
|
use codec::Encode;
|
||||||
use frame_benchmarking::{benchmarks, account, whitelisted_caller, impl_benchmark_test_suite};
|
use frame_benchmarking::{benchmarks, account, whitelisted_caller, impl_benchmark_test_suite};
|
||||||
use frame_system::{Pallet as System, RawOrigin};
|
use frame_system::{Pallet as System, RawOrigin};
|
||||||
use parity_wasm::elements::{Instruction, ValueType, BlockType};
|
use parity_wasm::elements::{Instruction, ValueType, BlockType};
|
||||||
@@ -313,7 +314,7 @@ benchmarks! {
|
|||||||
let WasmModule { code, hash, .. } = WasmModule::<T>::sized(c * 1024);
|
let WasmModule { code, hash, .. } = WasmModule::<T>::sized(c * 1024);
|
||||||
Contracts::<T>::store_code_raw(code)?;
|
Contracts::<T>::store_code_raw(code)?;
|
||||||
let mut module = PrefabWasmModule::from_storage_noinstr(hash)?;
|
let mut module = PrefabWasmModule::from_storage_noinstr(hash)?;
|
||||||
let schedule = Contracts::<T>::current_schedule();
|
let schedule = <CurrentSchedule<T>>::get();
|
||||||
}: {
|
}: {
|
||||||
Contracts::<T>::reinstrument_module(&mut module, &schedule)?;
|
Contracts::<T>::reinstrument_module(&mut module, &schedule)?;
|
||||||
}
|
}
|
||||||
@@ -936,7 +937,7 @@ benchmarks! {
|
|||||||
seal_random {
|
seal_random {
|
||||||
let r in 0 .. API_BENCHMARK_BATCHES;
|
let r in 0 .. API_BENCHMARK_BATCHES;
|
||||||
let pages = code::max_pages::<T>();
|
let pages = code::max_pages::<T>();
|
||||||
let subject_len = Contracts::<T>::current_schedule().limits.subject_len;
|
let subject_len = <CurrentSchedule<T>>::get().limits.subject_len;
|
||||||
assert!(subject_len < 1024);
|
assert!(subject_len < 1024);
|
||||||
let code = WasmModule::<T>::from(ModuleDefinition {
|
let code = WasmModule::<T>::from(ModuleDefinition {
|
||||||
memory: Some(ImportedMemory::max::<T>()),
|
memory: Some(ImportedMemory::max::<T>()),
|
||||||
@@ -992,7 +993,7 @@ benchmarks! {
|
|||||||
// `t`: Number of topics
|
// `t`: Number of topics
|
||||||
// `n`: Size of event payload in kb
|
// `n`: Size of event payload in kb
|
||||||
seal_deposit_event_per_topic_and_kb {
|
seal_deposit_event_per_topic_and_kb {
|
||||||
let t in 0 .. Contracts::<T>::current_schedule().limits.event_topics;
|
let t in 0 .. <CurrentSchedule<T>>::get().limits.event_topics;
|
||||||
let n in 0 .. T::MaxValueSize::get() / 1024;
|
let n in 0 .. T::MaxValueSize::get() / 1024;
|
||||||
let mut topics = (0..API_BENCHMARK_BATCH_SIZE)
|
let mut topics = (0..API_BENCHMARK_BATCH_SIZE)
|
||||||
.map(|n| (n * t..n * t + t).map(|i| T::Hashing::hash_of(&i)).collect::<Vec<_>>().encode())
|
.map(|n| (n * t..n * t + t).map(|i| T::Hashing::hash_of(&i)).collect::<Vec<_>>().encode())
|
||||||
@@ -1922,7 +1923,7 @@ benchmarks! {
|
|||||||
|
|
||||||
// w_br_table_per_entry = w_bench
|
// w_br_table_per_entry = w_bench
|
||||||
instr_br_table_per_entry {
|
instr_br_table_per_entry {
|
||||||
let e in 1 .. Contracts::<T>::current_schedule().limits.br_table_size;
|
let e in 1 .. <CurrentSchedule<T>>::get().limits.br_table_size;
|
||||||
let entry: Vec<u32> = [0, 1].iter()
|
let entry: Vec<u32> = [0, 1].iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.cycle()
|
.cycle()
|
||||||
@@ -1978,7 +1979,7 @@ benchmarks! {
|
|||||||
// w_call_indrect = w_bench - 3 * w_param
|
// w_call_indrect = w_bench - 3 * w_param
|
||||||
instr_call_indirect {
|
instr_call_indirect {
|
||||||
let r in 0 .. INSTR_BENCHMARK_BATCHES;
|
let r in 0 .. INSTR_BENCHMARK_BATCHES;
|
||||||
let num_elements = Contracts::<T>::current_schedule().limits.table_size;
|
let num_elements = <CurrentSchedule<T>>::get().limits.table_size;
|
||||||
use self::code::TableSegment;
|
use self::code::TableSegment;
|
||||||
let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
|
let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
|
||||||
// We need to make use of the stack here in order to trigger stack height
|
// We need to make use of the stack here in order to trigger stack height
|
||||||
@@ -2008,8 +2009,8 @@ benchmarks! {
|
|||||||
// linearly depend on the amount of parameters to this function.
|
// linearly depend on the amount of parameters to this function.
|
||||||
// Please note that this is not necessary with a direct call.
|
// Please note that this is not necessary with a direct call.
|
||||||
instr_call_indirect_per_param {
|
instr_call_indirect_per_param {
|
||||||
let p in 0 .. Contracts::<T>::current_schedule().limits.parameters;
|
let p in 0 .. <CurrentSchedule<T>>::get().limits.parameters;
|
||||||
let num_elements = Contracts::<T>::current_schedule().limits.table_size;
|
let num_elements = <CurrentSchedule<T>>::get().limits.table_size;
|
||||||
use self::code::TableSegment;
|
use self::code::TableSegment;
|
||||||
let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
|
let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
|
||||||
// We need to make use of the stack here in order to trigger stack height
|
// We need to make use of the stack here in order to trigger stack height
|
||||||
@@ -2039,7 +2040,7 @@ benchmarks! {
|
|||||||
// w_local_get = w_bench - 1 * w_param
|
// w_local_get = w_bench - 1 * w_param
|
||||||
instr_local_get {
|
instr_local_get {
|
||||||
let r in 0 .. INSTR_BENCHMARK_BATCHES;
|
let r in 0 .. INSTR_BENCHMARK_BATCHES;
|
||||||
let max_locals = Contracts::<T>::current_schedule().limits.stack_height;
|
let max_locals = <CurrentSchedule<T>>::get().limits.stack_height;
|
||||||
let mut call_body = body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec
|
||||||
|
//! on how to use a chain extension in order to provide new features to ink! contracts.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Error,
|
Error,
|
||||||
@@ -141,8 +147,8 @@ pub enum RetVal {
|
|||||||
|
|
||||||
/// Grants the chain extension access to its parameters and execution environment.
|
/// Grants the chain extension access to its parameters and execution environment.
|
||||||
///
|
///
|
||||||
/// It uses the typestate pattern to enforce the correct usage of the parameters passed
|
/// It uses [typestate programming](https://docs.rust-embedded.org/book/static-guarantees/typestate-programming.html)
|
||||||
/// to the chain extension.
|
/// to enforce the correct usage of the parameters passed to the chain extension.
|
||||||
pub struct Environment<'a, 'b, E: Ext, S: state::State> {
|
pub struct Environment<'a, 'b, E: Ext, S: state::State> {
|
||||||
/// The actual data of this type.
|
/// The actual data of this type.
|
||||||
inner: Inner<'a, 'b, E>,
|
inner: Inner<'a, 'b, E>,
|
||||||
@@ -376,6 +382,8 @@ mod state {
|
|||||||
pub trait BufIn: State {}
|
pub trait BufIn: State {}
|
||||||
pub trait BufOut: State {}
|
pub trait BufOut: State {}
|
||||||
|
|
||||||
|
/// The initial state of an [`Environment`](`super::Environment`).
|
||||||
|
/// See [typestate programming](https://docs.rust-embedded.org/book/static-guarantees/typestate-programming.html).
|
||||||
pub enum Init {}
|
pub enum Init {}
|
||||||
pub enum OnlyIn {}
|
pub enum OnlyIn {}
|
||||||
pub enum PrimInBufOut {}
|
pub enum PrimInBufOut {}
|
||||||
|
|||||||
@@ -943,7 +943,7 @@ mod tests {
|
|||||||
test_utils::{place_contract, set_balance, get_balance},
|
test_utils::{place_contract, set_balance, get_balance},
|
||||||
},
|
},
|
||||||
exec::ExportedFunction::*,
|
exec::ExportedFunction::*,
|
||||||
Error, Weight,
|
Error, Weight, CurrentSchedule,
|
||||||
};
|
};
|
||||||
use sp_runtime::DispatchError;
|
use sp_runtime::DispatchError;
|
||||||
use assert_matches::assert_matches;
|
use assert_matches::assert_matches;
|
||||||
@@ -1139,7 +1139,7 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let schedule = Contracts::current_schedule();
|
let schedule = <CurrentSchedule<Test>>::get();
|
||||||
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
||||||
place_contract(&BOB, exec_ch);
|
place_contract(&BOB, exec_ch);
|
||||||
|
|
||||||
@@ -1189,7 +1189,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let schedule = Contracts::current_schedule();
|
let schedule = <CurrentSchedule<Test>>::get();
|
||||||
let mut ctx = MockContext::top_level(origin.clone(), &schedule);
|
let mut ctx = MockContext::top_level(origin.clone(), &schedule);
|
||||||
place_contract(&BOB, return_ch);
|
place_contract(&BOB, return_ch);
|
||||||
set_balance(&origin, 100);
|
set_balance(&origin, 100);
|
||||||
@@ -1249,7 +1249,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let schedule = Contracts::current_schedule();
|
let schedule = <CurrentSchedule<Test>>::get();
|
||||||
let mut ctx = MockContext::top_level(origin, &schedule);
|
let mut ctx = MockContext::top_level(origin, &schedule);
|
||||||
place_contract(&BOB, return_ch);
|
place_contract(&BOB, return_ch);
|
||||||
|
|
||||||
@@ -1278,7 +1278,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let schedule = Contracts::current_schedule();
|
let schedule = <CurrentSchedule<Test>>::get();
|
||||||
let mut ctx = MockContext::top_level(origin, &schedule);
|
let mut ctx = MockContext::top_level(origin, &schedule);
|
||||||
place_contract(&BOB, return_ch);
|
place_contract(&BOB, return_ch);
|
||||||
|
|
||||||
@@ -1304,7 +1304,7 @@ mod tests {
|
|||||||
|
|
||||||
// This one tests passing the input data into a contract via call.
|
// This one tests passing the input data into a contract via call.
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let schedule = Contracts::current_schedule();
|
let schedule = <CurrentSchedule<Test>>::get();
|
||||||
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
||||||
place_contract(&BOB, input_data_ch);
|
place_contract(&BOB, input_data_ch);
|
||||||
|
|
||||||
@@ -1327,7 +1327,7 @@ mod tests {
|
|||||||
|
|
||||||
// This one tests passing the input data into a contract via instantiate.
|
// This one tests passing the input data into a contract via instantiate.
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let schedule = Contracts::current_schedule();
|
let schedule = <CurrentSchedule<Test>>::get();
|
||||||
let subsistence = Contracts::<Test>::subsistence_threshold();
|
let subsistence = Contracts::<Test>::subsistence_threshold();
|
||||||
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
||||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||||
@@ -1380,7 +1380,7 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let schedule = Contracts::current_schedule();
|
let schedule = <CurrentSchedule<Test>>::get();
|
||||||
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
||||||
set_balance(&BOB, 1);
|
set_balance(&BOB, 1);
|
||||||
place_contract(&BOB, recurse_ch);
|
place_contract(&BOB, recurse_ch);
|
||||||
@@ -1428,7 +1428,7 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let schedule = Contracts::current_schedule();
|
let schedule = <CurrentSchedule<Test>>::get();
|
||||||
let mut ctx = MockContext::top_level(origin.clone(), &schedule);
|
let mut ctx = MockContext::top_level(origin.clone(), &schedule);
|
||||||
place_contract(&dest, bob_ch);
|
place_contract(&dest, bob_ch);
|
||||||
place_contract(&CHARLIE, charlie_ch);
|
place_contract(&CHARLIE, charlie_ch);
|
||||||
@@ -1466,7 +1466,7 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let schedule = Contracts::current_schedule();
|
let schedule = <CurrentSchedule<Test>>::get();
|
||||||
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
||||||
place_contract(&BOB, bob_ch);
|
place_contract(&BOB, bob_ch);
|
||||||
place_contract(&CHARLIE, charlie_ch);
|
place_contract(&CHARLIE, charlie_ch);
|
||||||
@@ -1487,7 +1487,7 @@ mod tests {
|
|||||||
let dummy_ch = MockLoader::insert(Constructor, |_, _| exec_success());
|
let dummy_ch = MockLoader::insert(Constructor, |_, _| exec_success());
|
||||||
|
|
||||||
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
|
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
|
||||||
let schedule = Contracts::current_schedule();
|
let schedule = <CurrentSchedule<Test>>::get();
|
||||||
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
||||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||||
let executable = MockExecutable::from_storage(
|
let executable = MockExecutable::from_storage(
|
||||||
@@ -1515,7 +1515,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
|
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
|
||||||
let schedule = Contracts::current_schedule();
|
let schedule = <CurrentSchedule<Test>>::get();
|
||||||
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
||||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||||
let executable = MockExecutable::from_storage(
|
let executable = MockExecutable::from_storage(
|
||||||
@@ -1551,7 +1551,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
|
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
|
||||||
let schedule = Contracts::current_schedule();
|
let schedule = <CurrentSchedule<Test>>::get();
|
||||||
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
||||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||||
let executable = MockExecutable::from_storage(
|
let executable = MockExecutable::from_storage(
|
||||||
@@ -1599,7 +1599,7 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
|
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
|
||||||
let schedule = Contracts::current_schedule();
|
let schedule = <CurrentSchedule<Test>>::get();
|
||||||
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
||||||
set_balance(&ALICE, Contracts::<Test>::subsistence_threshold() * 100);
|
set_balance(&ALICE, Contracts::<Test>::subsistence_threshold() * 100);
|
||||||
place_contract(&BOB, instantiator_ch);
|
place_contract(&BOB, instantiator_ch);
|
||||||
@@ -1648,7 +1648,7 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
|
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
|
||||||
let schedule = Contracts::current_schedule();
|
let schedule = <CurrentSchedule<Test>>::get();
|
||||||
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
||||||
set_balance(&ALICE, 1000);
|
set_balance(&ALICE, 1000);
|
||||||
set_balance(&BOB, 100);
|
set_balance(&BOB, 100);
|
||||||
@@ -1676,7 +1676,7 @@ mod tests {
|
|||||||
.existential_deposit(15)
|
.existential_deposit(15)
|
||||||
.build()
|
.build()
|
||||||
.execute_with(|| {
|
.execute_with(|| {
|
||||||
let schedule = Contracts::current_schedule();
|
let schedule = <CurrentSchedule<Test>>::get();
|
||||||
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
||||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||||
let executable = MockExecutable::from_storage(
|
let executable = MockExecutable::from_storage(
|
||||||
@@ -1715,7 +1715,7 @@ mod tests {
|
|||||||
|
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let subsistence = Contracts::<Test>::subsistence_threshold();
|
let subsistence = Contracts::<Test>::subsistence_threshold();
|
||||||
let schedule = Contracts::current_schedule();
|
let schedule = <CurrentSchedule<Test>>::get();
|
||||||
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
||||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||||
let executable = MockExecutable::from_storage(
|
let executable = MockExecutable::from_storage(
|
||||||
@@ -1747,7 +1747,7 @@ mod tests {
|
|||||||
|
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let subsistence = Contracts::<Test>::subsistence_threshold();
|
let subsistence = Contracts::<Test>::subsistence_threshold();
|
||||||
let schedule = Contracts::current_schedule();
|
let schedule = <CurrentSchedule<Test>>::get();
|
||||||
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
||||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||||
set_balance(&ALICE, subsistence * 10);
|
set_balance(&ALICE, subsistence * 10);
|
||||||
@@ -1795,7 +1795,7 @@ mod tests {
|
|||||||
|
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let subsistence = Contracts::<Test>::subsistence_threshold();
|
let subsistence = Contracts::<Test>::subsistence_threshold();
|
||||||
let schedule = Contracts::current_schedule();
|
let schedule = <CurrentSchedule<Test>>::get();
|
||||||
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
let mut ctx = MockContext::top_level(ALICE, &schedule);
|
||||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||||
set_balance(&ALICE, subsistence * 100);
|
set_balance(&ALICE, subsistence * 100);
|
||||||
|
|||||||
@@ -24,12 +24,12 @@
|
|||||||
//!
|
//!
|
||||||
//! ## Overview
|
//! ## Overview
|
||||||
//!
|
//!
|
||||||
//! This module extends accounts based on the `Currency` trait to have smart-contract functionality. It can
|
//! This module extends accounts based on the [`Currency`] trait to have smart-contract functionality. It can
|
||||||
//! be used with other modules that implement accounts based on `Currency`. These "smart-contract accounts"
|
//! be used with other modules that implement accounts based on [`Currency`]. These "smart-contract accounts"
|
||||||
//! have the ability to instantiate smart-contracts and make calls to other contract and non-contract accounts.
|
//! have the ability to instantiate smart-contracts and make calls to other contract and non-contract accounts.
|
||||||
//!
|
//!
|
||||||
//! The smart-contract code is stored once in a `code_cache`, and later retrievable via its `code_hash`.
|
//! The smart-contract code is stored once in a code cache, and later retrievable via its hash.
|
||||||
//! This means that multiple smart-contracts can be instantiated from the same `code_cache`, without replicating
|
//! This means that multiple smart-contracts can be instantiated from the same hash, without replicating
|
||||||
//! the code each time.
|
//! the code each time.
|
||||||
//!
|
//!
|
||||||
//! When a smart-contract is called, its associated code is retrieved via the code hash and gets executed.
|
//! When a smart-contract is called, its associated code is retrieved via the code hash and gets executed.
|
||||||
@@ -59,12 +59,17 @@
|
|||||||
//!
|
//!
|
||||||
//! ### Dispatchable functions
|
//! ### Dispatchable functions
|
||||||
//!
|
//!
|
||||||
//! * `instantiate_with_code` - Deploys a new contract from the supplied wasm binary, optionally transferring
|
//! * [`Pallet::update_schedule`] -
|
||||||
//! some balance. This instantiates a new smart contract account and calls its contract deploy
|
//! ([Root Origin](https://substrate.dev/docs/en/knowledgebase/runtime/origin) Only) -
|
||||||
//! handler to initialize the contract.
|
//! Set a new [`Schedule`].
|
||||||
//! * `instantiate` - The same as `instantiate_with_code` but instead of uploading new code an
|
//! * [`Pallet::instantiate_with_code`] - Deploys a new contract from the supplied wasm binary,
|
||||||
//! existing `code_hash` is supplied.
|
//! optionally transferring
|
||||||
//! * `call` - Makes a call to an account, optionally transferring some balance.
|
//! some balance. This instantiates a new smart contract account with the supplied code and
|
||||||
|
//! calls its constructor to initialize the contract.
|
||||||
|
//! * [`Pallet::instantiate`] - The same as `instantiate_with_code` but instead of uploading new
|
||||||
|
//! code an existing `code_hash` is supplied.
|
||||||
|
//! * [`Pallet::call`] - Makes a call to an account, optionally transferring some balance.
|
||||||
|
//! * [`Pallet::claim_surcharge`] - Evict a contract that cannot pay rent anymore.
|
||||||
//!
|
//!
|
||||||
//! ## Usage
|
//! ## Usage
|
||||||
//!
|
//!
|
||||||
@@ -98,29 +103,24 @@ pub mod weights;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
pub use crate::{
|
pub use crate::{pallet::*, schedule::Schedule};
|
||||||
wasm::PrefabWasmModule,
|
|
||||||
schedule::{Schedule, HostFnWeights, InstructionWeights, Limits},
|
|
||||||
pallet::*,
|
|
||||||
};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
gas::GasMeter,
|
gas::GasMeter,
|
||||||
exec::{ExecutionContext, Executable},
|
exec::{ExecutionContext, Executable},
|
||||||
rent::Rent,
|
rent::Rent,
|
||||||
storage::{Storage, DeletedContract},
|
storage::{Storage, DeletedContract, ContractInfo, AliveContractInfo, TombstoneContractInfo},
|
||||||
weights::WeightInfo,
|
weights::WeightInfo,
|
||||||
|
wasm::PrefabWasmModule,
|
||||||
};
|
};
|
||||||
use sp_core::crypto::UncheckedFrom;
|
use sp_core::crypto::UncheckedFrom;
|
||||||
use sp_std::{prelude::*, marker::PhantomData, fmt::Debug};
|
use sp_std::prelude::*;
|
||||||
use codec::{Codec, Encode, Decode};
|
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
traits::{
|
traits::{
|
||||||
Hash, StaticLookup, MaybeSerializeDeserialize, Member, Convert, Saturating, Zero,
|
Hash, StaticLookup, Convert, Saturating, Zero,
|
||||||
},
|
},
|
||||||
RuntimeDebug, Perbill,
|
Perbill,
|
||||||
};
|
};
|
||||||
use frame_support::{
|
use frame_support::{
|
||||||
storage::child::ChildInfo,
|
|
||||||
traits::{OnUnbalanced, Currency, Get, Time, Randomness},
|
traits::{OnUnbalanced, Currency, Get, Time, Randomness},
|
||||||
weights::{Weight, PostDispatchInfo, WithPostDispatchInfo},
|
weights::{Weight, PostDispatchInfo, WithPostDispatchInfo},
|
||||||
};
|
};
|
||||||
@@ -129,16 +129,12 @@ use pallet_contracts_primitives::{
|
|||||||
RentProjectionResult, GetStorageResult, ContractAccessError, ContractExecResult,
|
RentProjectionResult, GetStorageResult, ContractAccessError, ContractExecResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type CodeHash<T> = <T as frame_system::Config>::Hash;
|
type CodeHash<T> = <T as frame_system::Config>::Hash;
|
||||||
pub type TrieId = Vec<u8>;
|
type TrieId = Vec<u8>;
|
||||||
pub type BalanceOf<T> =
|
type BalanceOf<T> =
|
||||||
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
|
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
|
||||||
pub type NegativeImbalanceOf<T> =
|
type NegativeImbalanceOf<T> =
|
||||||
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
|
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
|
||||||
pub type AliveContractInfo<T> =
|
|
||||||
RawAliveContractInfo<CodeHash<T>, BalanceOf<T>, <T as frame_system::Config>::BlockNumber>;
|
|
||||||
pub type TombstoneContractInfo<T> =
|
|
||||||
RawTombstoneContractInfo<<T as frame_system::Config>::Hash, <T as frame_system::Config>::Hashing>;
|
|
||||||
|
|
||||||
#[frame_support::pallet]
|
#[frame_support::pallet]
|
||||||
pub mod pallet {
|
pub mod pallet {
|
||||||
@@ -248,7 +244,6 @@ pub mod pallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[pallet::pallet]
|
#[pallet::pallet]
|
||||||
#[pallet::generate_store(pub(super) trait Store)]
|
|
||||||
pub struct Pallet<T>(PhantomData<T>);
|
pub struct Pallet<T>(PhantomData<T>);
|
||||||
|
|
||||||
#[pallet::hooks]
|
#[pallet::hooks]
|
||||||
@@ -290,7 +285,7 @@ pub mod pallet {
|
|||||||
schedule: Schedule<T>
|
schedule: Schedule<T>
|
||||||
) -> DispatchResultWithPostInfo {
|
) -> DispatchResultWithPostInfo {
|
||||||
ensure_root(origin)?;
|
ensure_root(origin)?;
|
||||||
if <Pallet<T>>::current_schedule().version > schedule.version {
|
if <CurrentSchedule<T>>::get().version > schedule.version {
|
||||||
Err(Error::<T>::InvalidScheduleVersion)?
|
Err(Error::<T>::InvalidScheduleVersion)?
|
||||||
}
|
}
|
||||||
Self::deposit_event(Event::ScheduleUpdated(schedule.version));
|
Self::deposit_event(Event::ScheduleUpdated(schedule.version));
|
||||||
@@ -316,7 +311,7 @@ pub mod pallet {
|
|||||||
let origin = ensure_signed(origin)?;
|
let origin = ensure_signed(origin)?;
|
||||||
let dest = T::Lookup::lookup(dest)?;
|
let dest = T::Lookup::lookup(dest)?;
|
||||||
let mut gas_meter = GasMeter::new(gas_limit);
|
let mut gas_meter = GasMeter::new(gas_limit);
|
||||||
let schedule = <Pallet<T>>::current_schedule();
|
let schedule = <CurrentSchedule<T>>::get();
|
||||||
let mut ctx = ExecutionContext::<T, PrefabWasmModule<T>>::top_level(origin, &schedule);
|
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 ctx.call(dest, value, &mut gas_meter, data) {
|
||||||
Ok((output, len)) => (Ok(output), len),
|
Ok((output, len)) => (Ok(output), len),
|
||||||
@@ -365,7 +360,7 @@ pub mod pallet {
|
|||||||
let code_len = code.len() as u32;
|
let code_len = code.len() as u32;
|
||||||
ensure!(code_len <= T::MaxCodeSize::get(), Error::<T>::CodeTooLarge);
|
ensure!(code_len <= T::MaxCodeSize::get(), Error::<T>::CodeTooLarge);
|
||||||
let mut gas_meter = GasMeter::new(gas_limit);
|
let mut gas_meter = GasMeter::new(gas_limit);
|
||||||
let schedule = <Pallet<T>>::current_schedule();
|
let schedule = <CurrentSchedule<T>>::get();
|
||||||
let executable = PrefabWasmModule::from_code(code, &schedule)?;
|
let executable = PrefabWasmModule::from_code(code, &schedule)?;
|
||||||
let code_len = executable.code_len();
|
let code_len = executable.code_len();
|
||||||
ensure!(code_len <= T::MaxCodeSize::get(), Error::<T>::CodeTooLarge);
|
ensure!(code_len <= T::MaxCodeSize::get(), Error::<T>::CodeTooLarge);
|
||||||
@@ -397,7 +392,7 @@ pub mod pallet {
|
|||||||
) -> DispatchResultWithPostInfo {
|
) -> DispatchResultWithPostInfo {
|
||||||
let origin = ensure_signed(origin)?;
|
let origin = ensure_signed(origin)?;
|
||||||
let mut gas_meter = GasMeter::new(gas_limit);
|
let mut gas_meter = GasMeter::new(gas_limit);
|
||||||
let schedule = <Pallet<T>>::current_schedule();
|
let schedule = <CurrentSchedule<T>>::get();
|
||||||
let executable = PrefabWasmModule::from_storage(code_hash, &schedule, &mut gas_meter)?;
|
let executable = PrefabWasmModule::from_storage(code_hash, &schedule, &mut gas_meter)?;
|
||||||
let mut ctx = ExecutionContext::<T, PrefabWasmModule<T>>::top_level(origin, &schedule);
|
let mut ctx = ExecutionContext::<T, PrefabWasmModule<T>>::top_level(origin, &schedule);
|
||||||
let code_len = executable.code_len();
|
let code_len = executable.code_len();
|
||||||
@@ -614,33 +609,33 @@ pub mod pallet {
|
|||||||
|
|
||||||
/// Current cost schedule for contracts.
|
/// Current cost schedule for contracts.
|
||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
#[pallet::getter(fn current_schedule)]
|
pub(crate) type CurrentSchedule<T: Config> = StorageValue<_, Schedule<T>, ValueQuery>;
|
||||||
pub(super) type CurrentSchedule<T: Config> = StorageValue<_, Schedule<T>, ValueQuery>;
|
|
||||||
|
|
||||||
/// A mapping from an original code hash to the original code, untouched by instrumentation.
|
/// A mapping from an original code hash to the original code, untouched by instrumentation.
|
||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
pub type PristineCode<T: Config> = StorageMap<_, Identity, CodeHash<T>, Vec<u8>>;
|
pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, CodeHash<T>, Vec<u8>>;
|
||||||
|
|
||||||
/// A mapping between an original code hash and instrumented wasm code, ready for execution.
|
/// A mapping between an original code hash and instrumented wasm code, ready for execution.
|
||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
pub type CodeStorage<T: Config> = StorageMap<_, Identity, CodeHash<T>, PrefabWasmModule<T>>;
|
pub(crate) type CodeStorage<T: Config> = StorageMap<_, Identity, CodeHash<T>, PrefabWasmModule<T>>;
|
||||||
|
|
||||||
/// The subtrie counter.
|
/// The subtrie counter.
|
||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
pub type AccountCounter<T: Config> = StorageValue<_, u64, ValueQuery>;
|
pub(crate) type AccountCounter<T: Config> = StorageValue<_, u64, ValueQuery>;
|
||||||
|
|
||||||
/// The code associated with a given account.
|
/// The code associated with a given account.
|
||||||
///
|
///
|
||||||
/// TWOX-NOTE: SAFE since `AccountId` is a secure hash.
|
/// TWOX-NOTE: SAFE since `AccountId` is a secure hash.
|
||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
pub type ContractInfoOf<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, ContractInfo<T>>;
|
pub(crate) type ContractInfoOf<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, ContractInfo<T>>;
|
||||||
|
|
||||||
/// Evicted contracts that await child trie deletion.
|
/// Evicted contracts that await child trie deletion.
|
||||||
///
|
///
|
||||||
/// Child trie deletion is a heavy operation depending on the amount of storage items
|
/// Child trie deletion is a heavy operation depending on the amount of storage items
|
||||||
/// stored in said trie. Therefore this operation is performed lazily in `on_initialize`.
|
/// stored in said trie. Therefore this operation is performed lazily in `on_initialize`.
|
||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
pub type DeletionQueue<T: Config> = StorageValue<_, Vec<DeletedContract>, ValueQuery>;
|
pub(crate) type DeletionQueue<T: Config> = StorageValue<_, Vec<DeletedContract>, ValueQuery>;
|
||||||
|
|
||||||
|
|
||||||
#[pallet::genesis_config]
|
#[pallet::genesis_config]
|
||||||
pub struct GenesisConfig<T: Config> {
|
pub struct GenesisConfig<T: Config> {
|
||||||
@@ -683,7 +678,7 @@ where
|
|||||||
input_data: Vec<u8>,
|
input_data: Vec<u8>,
|
||||||
) -> ContractExecResult {
|
) -> ContractExecResult {
|
||||||
let mut gas_meter = GasMeter::new(gas_limit);
|
let mut gas_meter = GasMeter::new(gas_limit);
|
||||||
let schedule = <Pallet<T>>::current_schedule();
|
let schedule = <CurrentSchedule<T>>::get();
|
||||||
let mut ctx = ExecutionContext::<T, PrefabWasmModule<T>>::top_level(origin, &schedule);
|
let mut ctx = ExecutionContext::<T, PrefabWasmModule<T>>::top_level(origin, &schedule);
|
||||||
let result = ctx.call(dest, value, &mut gas_meter, input_data);
|
let result = ctx.call(dest, value, &mut gas_meter, input_data);
|
||||||
let gas_consumed = gas_meter.gas_spent();
|
let gas_consumed = gas_meter.gas_spent();
|
||||||
@@ -743,10 +738,24 @@ where
|
|||||||
T::Currency::minimum_balance().saturating_add(T::TombstoneDeposit::get())
|
T::Currency::minimum_balance().saturating_add(T::TombstoneDeposit::get())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The in-memory size in bytes of the data structure associated with each contract.
|
||||||
|
///
|
||||||
|
/// The data structure is also put into storage for each contract. The in-storage size
|
||||||
|
/// is never larger than the in-memory representation and usually smaller due to compact
|
||||||
|
/// encoding and lack of padding.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// This returns the in-memory size because the in-storage size (SCALE encoded) cannot
|
||||||
|
/// be efficiently determined. Treat this as an upper bound of the in-storage size.
|
||||||
|
pub fn contract_info_size() -> u32 {
|
||||||
|
sp_std::mem::size_of::<ContractInfo<T>>() as u32
|
||||||
|
}
|
||||||
|
|
||||||
/// Store code for benchmarks which does not check nor instrument the code.
|
/// Store code for benchmarks which does not check nor instrument the code.
|
||||||
#[cfg(feature = "runtime-benchmarks")]
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
fn store_code_raw(code: Vec<u8>) -> frame_support::dispatch::DispatchResult {
|
fn store_code_raw(code: Vec<u8>) -> frame_support::dispatch::DispatchResult {
|
||||||
let schedule = <Pallet<T>>::current_schedule();
|
let schedule = <CurrentSchedule<T>>::get();
|
||||||
PrefabWasmModule::store_code_unchecked(code, &schedule)?;
|
PrefabWasmModule::store_code_unchecked(code, &schedule)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -760,127 +769,3 @@ where
|
|||||||
self::wasm::reinstrument(module, schedule)
|
self::wasm::reinstrument(module, schedule)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information for managing an account and its sub trie abstraction.
|
|
||||||
/// This is the required info to cache for an account
|
|
||||||
#[derive(Encode, Decode, RuntimeDebug)]
|
|
||||||
pub enum ContractInfo<T: Config> {
|
|
||||||
Alive(AliveContractInfo<T>),
|
|
||||||
Tombstone(TombstoneContractInfo<T>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Config> ContractInfo<T> {
|
|
||||||
/// If contract is alive then return some alive info
|
|
||||||
pub fn get_alive(self) -> Option<AliveContractInfo<T>> {
|
|
||||||
if let ContractInfo::Alive(alive) = self {
|
|
||||||
Some(alive)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// If contract is alive then return some reference to alive info
|
|
||||||
pub fn as_alive(&self) -> Option<&AliveContractInfo<T>> {
|
|
||||||
if let ContractInfo::Alive(ref alive) = self {
|
|
||||||
Some(alive)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// If contract is alive then return some mutable reference to alive info
|
|
||||||
pub fn as_alive_mut(&mut self) -> Option<&mut AliveContractInfo<T>> {
|
|
||||||
if let ContractInfo::Alive(ref mut alive) = self {
|
|
||||||
Some(alive)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If contract is tombstone then return some tombstone info
|
|
||||||
pub fn get_tombstone(self) -> Option<TombstoneContractInfo<T>> {
|
|
||||||
if let ContractInfo::Tombstone(tombstone) = self {
|
|
||||||
Some(tombstone)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// If contract is tombstone then return some reference to tombstone info
|
|
||||||
pub fn as_tombstone(&self) -> Option<&TombstoneContractInfo<T>> {
|
|
||||||
if let ContractInfo::Tombstone(ref tombstone) = self {
|
|
||||||
Some(tombstone)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// If contract is tombstone then return some mutable reference to tombstone info
|
|
||||||
pub fn as_tombstone_mut(&mut self) -> Option<&mut TombstoneContractInfo<T>> {
|
|
||||||
if let ContractInfo::Tombstone(ref mut tombstone) = self {
|
|
||||||
Some(tombstone)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Information for managing an account and its sub trie abstraction.
|
|
||||||
/// This is the required info to cache for an account.
|
|
||||||
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
|
|
||||||
pub struct RawAliveContractInfo<CodeHash, Balance, BlockNumber> {
|
|
||||||
/// Unique ID for the subtree encoded as a bytes vector.
|
|
||||||
pub trie_id: TrieId,
|
|
||||||
/// The total number of bytes used by this contract.
|
|
||||||
///
|
|
||||||
/// It is a sum of each key-value pair stored by this contract.
|
|
||||||
pub storage_size: u32,
|
|
||||||
/// The total number of key-value pairs in storage of this contract.
|
|
||||||
pub pair_count: u32,
|
|
||||||
/// The code associated with a given account.
|
|
||||||
pub code_hash: CodeHash,
|
|
||||||
/// Pay rent at most up to this value.
|
|
||||||
pub rent_allowance: Balance,
|
|
||||||
/// The amount of rent that was payed by the contract over its whole lifetime.
|
|
||||||
///
|
|
||||||
/// A restored contract starts with a value of zero just like a new contract.
|
|
||||||
pub rent_payed: Balance,
|
|
||||||
/// Last block rent has been payed.
|
|
||||||
pub deduct_block: BlockNumber,
|
|
||||||
/// Last block child storage has been written.
|
|
||||||
pub last_write: Option<BlockNumber>,
|
|
||||||
/// This field is reserved for future evolution of format.
|
|
||||||
pub _reserved: Option<()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<CodeHash, Balance, BlockNumber> RawAliveContractInfo<CodeHash, Balance, BlockNumber> {
|
|
||||||
/// Associated child trie unique id is built from the hash part of the trie id.
|
|
||||||
pub fn child_trie_info(&self) -> ChildInfo {
|
|
||||||
child_trie_info(&self.trie_id[..])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Associated child trie unique id is built from the hash part of the trie id.
|
|
||||||
pub(crate) fn child_trie_info(trie_id: &[u8]) -> ChildInfo {
|
|
||||||
ChildInfo::new_default(trie_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug)]
|
|
||||||
pub struct RawTombstoneContractInfo<H, Hasher>(H, PhantomData<Hasher>);
|
|
||||||
|
|
||||||
impl<H, Hasher> RawTombstoneContractInfo<H, Hasher>
|
|
||||||
where
|
|
||||||
H: Member + MaybeSerializeDeserialize+ Debug
|
|
||||||
+ AsRef<[u8]> + AsMut<[u8]> + Copy + Default
|
|
||||||
+ sp_std::hash::Hash + Codec,
|
|
||||||
Hasher: Hash<Output=H>,
|
|
||||||
{
|
|
||||||
fn new(storage_root: &[u8], code_hash: H) -> Self {
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
storage_root.using_encoded(|encoded| buf.extend_from_slice(encoded));
|
|
||||||
buf.extend_from_slice(code_hash.as_ref());
|
|
||||||
RawTombstoneContractInfo(<Hasher as Hash>::hash(&buf[..]), PhantomData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Config> From<AliveContractInfo<T>> for ContractInfo<T> {
|
|
||||||
fn from(alive_info: AliveContractInfo<T>) -> Self {
|
|
||||||
Self::Alive(alive_info)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -39,7 +39,11 @@ pub const API_BENCHMARK_BATCH_SIZE: u32 = 100;
|
|||||||
/// as for `API_BENCHMARK_BATCH_SIZE`.
|
/// as for `API_BENCHMARK_BATCH_SIZE`.
|
||||||
pub const INSTR_BENCHMARK_BATCH_SIZE: u32 = 1_000;
|
pub const INSTR_BENCHMARK_BATCH_SIZE: u32 = 1_000;
|
||||||
|
|
||||||
/// Definition of the cost schedule and other parameterizations for wasm vm.
|
/// Definition of the cost schedule and other parameterizations for the wasm vm.
|
||||||
|
///
|
||||||
|
/// Its fields are private to the crate in order to allow addition of new contract
|
||||||
|
/// callable functions without bumping to a new major version. A genesis config should
|
||||||
|
/// rely on public functions of this type.
|
||||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
#[cfg_attr(feature = "std", serde(bound(serialize = "", deserialize = "")))]
|
#[cfg_attr(feature = "std", serde(bound(serialize = "", deserialize = "")))]
|
||||||
#[derive(Clone, Encode, Decode, PartialEq, Eq, ScheduleDebug)]
|
#[derive(Clone, Encode, Decode, PartialEq, Eq, ScheduleDebug)]
|
||||||
@@ -53,20 +57,20 @@ pub struct Schedule<T: Config> {
|
|||||||
/// of all contracts which are triggered by a version comparison on call.
|
/// of all contracts which are triggered by a version comparison on call.
|
||||||
/// Changes to other parts of the schedule should not increment the version in
|
/// Changes to other parts of the schedule should not increment the version in
|
||||||
/// order to avoid unnecessary re-instrumentations.
|
/// order to avoid unnecessary re-instrumentations.
|
||||||
pub version: u32,
|
pub(crate) version: u32,
|
||||||
|
|
||||||
/// Whether the `seal_println` function is allowed to be used contracts.
|
/// Whether the `seal_println` function is allowed to be used contracts.
|
||||||
/// MUST only be enabled for `dev` chains, NOT for production chains
|
/// MUST only be enabled for `dev` chains, NOT for production chains
|
||||||
pub enable_println: bool,
|
pub(crate) enable_println: bool,
|
||||||
|
|
||||||
/// Describes the upper limits on various metrics.
|
/// Describes the upper limits on various metrics.
|
||||||
pub limits: Limits,
|
pub(crate) limits: Limits,
|
||||||
|
|
||||||
/// The weights for individual wasm instructions.
|
/// The weights for individual wasm instructions.
|
||||||
pub instruction_weights: InstructionWeights<T>,
|
pub(crate) instruction_weights: InstructionWeights<T>,
|
||||||
|
|
||||||
/// The weights for each imported function a contract is allowed to call.
|
/// The weights for each imported function a contract is allowed to call.
|
||||||
pub host_fn_weights: HostFnWeights<T>,
|
pub(crate) host_fn_weights: HostFnWeights<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes the upper limits on various metrics.
|
/// Describes the upper limits on various metrics.
|
||||||
@@ -602,7 +606,21 @@ struct ScheduleRules<'a, T: Config> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Config> Schedule<T> {
|
impl<T: Config> Schedule<T> {
|
||||||
pub fn rules(&self, module: &elements::Module) -> impl rules::Rules + '_ {
|
/// Allow contracts to call `seal_println` in order to print messages to the console.
|
||||||
|
///
|
||||||
|
/// This should only ever be activated in development chains. The printed messages
|
||||||
|
/// can be observed on the console by setting the environment variable
|
||||||
|
/// `RUST_LOG=runtime=debug` when running the node.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// Is set to `false` by default.
|
||||||
|
pub fn enable_println(mut self, enable: bool) -> Self {
|
||||||
|
self.enable_println = enable;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn rules(&self, module: &elements::Module) -> impl rules::Rules + '_ {
|
||||||
ScheduleRules {
|
ScheduleRules {
|
||||||
schedule: &self,
|
schedule: &self,
|
||||||
params: module
|
params: module
|
||||||
|
|||||||
@@ -19,23 +19,131 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
exec::{AccountIdOf, StorageKey},
|
exec::{AccountIdOf, StorageKey},
|
||||||
AliveContractInfo, BalanceOf, CodeHash, ContractInfo, ContractInfoOf, Config, TrieId,
|
BalanceOf, CodeHash, ContractInfoOf, Config, TrieId,
|
||||||
AccountCounter, DeletionQueue, Error,
|
AccountCounter, DeletionQueue, Error,
|
||||||
weights::WeightInfo,
|
weights::WeightInfo,
|
||||||
};
|
};
|
||||||
use codec::{Encode, Decode};
|
use codec::{Codec, Encode, Decode};
|
||||||
use sp_std::prelude::*;
|
use sp_std::prelude::*;
|
||||||
use sp_std::marker::PhantomData;
|
use sp_std::{marker::PhantomData, fmt::Debug};
|
||||||
use sp_io::hashing::blake2_256;
|
use sp_io::hashing::blake2_256;
|
||||||
use sp_runtime::traits::{Bounded, Saturating, Zero};
|
use sp_runtime::{
|
||||||
|
RuntimeDebug,
|
||||||
|
traits::{Bounded, Saturating, Zero, Hash, Member, MaybeSerializeDeserialize},
|
||||||
|
};
|
||||||
use sp_core::crypto::UncheckedFrom;
|
use sp_core::crypto::UncheckedFrom;
|
||||||
use frame_support::{
|
use frame_support::{
|
||||||
dispatch::{DispatchError, DispatchResult},
|
dispatch::{DispatchError, DispatchResult},
|
||||||
storage::child::{self, KillChildStorageResult},
|
storage::child::{self, KillChildStorageResult, ChildInfo},
|
||||||
traits::Get,
|
traits::Get,
|
||||||
weights::Weight,
|
weights::Weight,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub type AliveContractInfo<T> =
|
||||||
|
RawAliveContractInfo<CodeHash<T>, BalanceOf<T>, <T as frame_system::Config>::BlockNumber>;
|
||||||
|
pub type TombstoneContractInfo<T> =
|
||||||
|
RawTombstoneContractInfo<<T as frame_system::Config>::Hash, <T as frame_system::Config>::Hashing>;
|
||||||
|
|
||||||
|
/// Information for managing an account and its sub trie abstraction.
|
||||||
|
/// This is the required info to cache for an account
|
||||||
|
#[derive(Encode, Decode, RuntimeDebug)]
|
||||||
|
pub enum ContractInfo<T: Config> {
|
||||||
|
Alive(AliveContractInfo<T>),
|
||||||
|
Tombstone(TombstoneContractInfo<T>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Config> ContractInfo<T> {
|
||||||
|
/// If contract is alive then return some alive info
|
||||||
|
pub fn get_alive(self) -> Option<AliveContractInfo<T>> {
|
||||||
|
if let ContractInfo::Alive(alive) = self {
|
||||||
|
Some(alive)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// If contract is alive then return some reference to alive info
|
||||||
|
pub fn as_alive(&self) -> Option<&AliveContractInfo<T>> {
|
||||||
|
if let ContractInfo::Alive(ref alive) = self {
|
||||||
|
Some(alive)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If contract is tombstone then return some tombstone info
|
||||||
|
pub fn get_tombstone(self) -> Option<TombstoneContractInfo<T>> {
|
||||||
|
if let ContractInfo::Tombstone(tombstone) = self {
|
||||||
|
Some(tombstone)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information for managing an account and its sub trie abstraction.
|
||||||
|
/// This is the required info to cache for an account.
|
||||||
|
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
|
||||||
|
pub struct RawAliveContractInfo<CodeHash, Balance, BlockNumber> {
|
||||||
|
/// Unique ID for the subtree encoded as a bytes vector.
|
||||||
|
pub trie_id: TrieId,
|
||||||
|
/// The total number of bytes used by this contract.
|
||||||
|
///
|
||||||
|
/// It is a sum of each key-value pair stored by this contract.
|
||||||
|
pub storage_size: u32,
|
||||||
|
/// The total number of key-value pairs in storage of this contract.
|
||||||
|
pub pair_count: u32,
|
||||||
|
/// The code associated with a given account.
|
||||||
|
pub code_hash: CodeHash,
|
||||||
|
/// Pay rent at most up to this value.
|
||||||
|
pub rent_allowance: Balance,
|
||||||
|
/// The amount of rent that was payed by the contract over its whole lifetime.
|
||||||
|
///
|
||||||
|
/// A restored contract starts with a value of zero just like a new contract.
|
||||||
|
pub rent_payed: Balance,
|
||||||
|
/// Last block rent has been payed.
|
||||||
|
pub deduct_block: BlockNumber,
|
||||||
|
/// Last block child storage has been written.
|
||||||
|
pub last_write: Option<BlockNumber>,
|
||||||
|
/// This field is reserved for future evolution of format.
|
||||||
|
pub _reserved: Option<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CodeHash, Balance, BlockNumber> RawAliveContractInfo<CodeHash, Balance, BlockNumber> {
|
||||||
|
/// Associated child trie unique id is built from the hash part of the trie id.
|
||||||
|
pub fn child_trie_info(&self) -> ChildInfo {
|
||||||
|
child_trie_info(&self.trie_id[..])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Associated child trie unique id is built from the hash part of the trie id.
|
||||||
|
fn child_trie_info(trie_id: &[u8]) -> ChildInfo {
|
||||||
|
ChildInfo::new_default(trie_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug)]
|
||||||
|
pub struct RawTombstoneContractInfo<H, Hasher>(H, PhantomData<Hasher>);
|
||||||
|
|
||||||
|
impl<H, Hasher> RawTombstoneContractInfo<H, Hasher>
|
||||||
|
where
|
||||||
|
H: Member + MaybeSerializeDeserialize+ Debug
|
||||||
|
+ AsRef<[u8]> + AsMut<[u8]> + Copy + Default
|
||||||
|
+ sp_std::hash::Hash + Codec,
|
||||||
|
Hasher: Hash<Output=H>,
|
||||||
|
{
|
||||||
|
pub fn new(storage_root: &[u8], code_hash: H) -> Self {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
storage_root.using_encoded(|encoded| buf.extend_from_slice(encoded));
|
||||||
|
buf.extend_from_slice(code_hash.as_ref());
|
||||||
|
RawTombstoneContractInfo(<Hasher as Hash>::hash(&buf[..]), PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Config> From<AliveContractInfo<T>> for ContractInfo<T> {
|
||||||
|
fn from(alive_info: AliveContractInfo<T>) -> Self {
|
||||||
|
Self::Alive(alive_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An error that means that the account requested either doesn't exist or represents a tombstone
|
/// An error that means that the account requested either doesn't exist or represents a tombstone
|
||||||
/// account.
|
/// account.
|
||||||
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
||||||
@@ -59,7 +167,7 @@ where
|
|||||||
/// The read is performed from the `trie_id` only. The `address` is not necessary. If the contract
|
/// The read is performed from the `trie_id` only. The `address` is not necessary. If the contract
|
||||||
/// doesn't store under the given `key` `None` is returned.
|
/// doesn't store under the given `key` `None` is returned.
|
||||||
pub fn read(trie_id: &TrieId, key: &StorageKey) -> Option<Vec<u8>> {
|
pub fn read(trie_id: &TrieId, key: &StorageKey) -> Option<Vec<u8>> {
|
||||||
child::get_raw(&crate::child_trie_info(&trie_id), &blake2_256(key))
|
child::get_raw(&child_trie_info(&trie_id), &blake2_256(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update a storage entry into a contract's kv storage.
|
/// Update a storage entry into a contract's kv storage.
|
||||||
@@ -87,7 +195,7 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
let hashed_key = blake2_256(key);
|
let hashed_key = blake2_256(key);
|
||||||
let child_trie_info = &crate::child_trie_info(&trie_id);
|
let child_trie_info = &child_trie_info(&trie_id);
|
||||||
|
|
||||||
let opt_prev_len = child::len(&child_trie_info, &hashed_key);
|
let opt_prev_len = child::len(&child_trie_info, &hashed_key);
|
||||||
|
|
||||||
@@ -257,7 +365,7 @@ where
|
|||||||
let trie = &mut queue[0];
|
let trie = &mut queue[0];
|
||||||
let pair_count = trie.pair_count;
|
let pair_count = trie.pair_count;
|
||||||
let outcome = child::kill_storage(
|
let outcome = child::kill_storage(
|
||||||
&crate::child_trie_info(&trie.trie_id),
|
&child_trie_info(&trie.trie_id),
|
||||||
Some(remaining_key_budget),
|
Some(remaining_key_budget),
|
||||||
);
|
);
|
||||||
if pair_count > remaining_key_budget {
|
if pair_count > remaining_key_budget {
|
||||||
@@ -290,7 +398,6 @@ where
|
|||||||
/// This generator uses inner counter for account id and applies the hash over `AccountId +
|
/// This generator uses inner counter for account id and applies the hash over `AccountId +
|
||||||
/// accountid_counter`.
|
/// accountid_counter`.
|
||||||
pub fn generate_trie_id(account_id: &AccountIdOf<T>) -> TrieId {
|
pub fn generate_trie_id(account_id: &AccountIdOf<T>) -> TrieId {
|
||||||
use sp_runtime::traits::Hash;
|
|
||||||
// Note that skipping a value due to error is not an issue here.
|
// Note that skipping a value due to error is not an issue here.
|
||||||
// We only need uniqueness, not sequence.
|
// We only need uniqueness, not sequence.
|
||||||
let new_seed = <AccountCounter<T>>::mutate(|v| {
|
let new_seed = <AccountCounter<T>>::mutate(|v| {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
BalanceOf, ContractInfo, ContractInfoOf, Pallet,
|
BalanceOf, ContractInfo, ContractInfoOf, Pallet,
|
||||||
RawAliveContractInfo, Config, Schedule,
|
Config, Schedule,
|
||||||
Error, storage::Storage,
|
Error, storage::Storage,
|
||||||
chain_extension::{
|
chain_extension::{
|
||||||
Result as ExtensionResult, Environment, ChainExtension, Ext, SysConfig, RetVal,
|
Result as ExtensionResult, Environment, ChainExtension, Ext, SysConfig, RetVal,
|
||||||
@@ -26,6 +26,7 @@ use crate::{
|
|||||||
exec::{AccountIdOf, Executable}, wasm::PrefabWasmModule,
|
exec::{AccountIdOf, Executable}, wasm::PrefabWasmModule,
|
||||||
weights::WeightInfo,
|
weights::WeightInfo,
|
||||||
wasm::ReturnCode as RuntimeReturnCode,
|
wasm::ReturnCode as RuntimeReturnCode,
|
||||||
|
storage::RawAliveContractInfo,
|
||||||
};
|
};
|
||||||
use assert_matches::assert_matches;
|
use assert_matches::assert_matches;
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
|
|||||||
@@ -519,7 +519,7 @@ pub mod benchmarking {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{exec::Ext, Limits};
|
use crate::{exec::Ext, schedule::Limits};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
impl fmt::Debug for PrefabWasmModule<crate::tests::Test> {
|
impl fmt::Debug for PrefabWasmModule<crate::tests::Test> {
|
||||||
|
|||||||
@@ -18,10 +18,11 @@
|
|||||||
//! Environment definition of the wasm smart-contract runtime.
|
//! Environment definition of the wasm smart-contract runtime.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
HostFnWeights, Config, CodeHash, BalanceOf, Error,
|
Config, CodeHash, BalanceOf, Error,
|
||||||
exec::{Ext, StorageKey, TopicOf},
|
exec::{Ext, StorageKey, TopicOf},
|
||||||
gas::{GasMeter, Token, ChargedAmount},
|
gas::{GasMeter, Token, ChargedAmount},
|
||||||
wasm::env_def::ConvertibleToWasm,
|
wasm::env_def::ConvertibleToWasm,
|
||||||
|
schedule::HostFnWeights,
|
||||||
};
|
};
|
||||||
use parity_wasm::elements::ValueType;
|
use parity_wasm::elements::ValueType;
|
||||||
use frame_support::{dispatch::DispatchError, ensure, traits::Get, weights::Weight};
|
use frame_support::{dispatch::DispatchError, ensure, traits::Get, weights::Weight};
|
||||||
|
|||||||
Reference in New Issue
Block a user