contracts: Allow indeterministic instructions off-chain (#12469)

* Allow indetermistic instructions off-chain

* Apply suggestions from code review

Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>

* fmt

Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>
This commit is contained in:
Alexander Theißen
2022-10-24 19:48:04 +02:00
committed by GitHub
parent d0dcf008ec
commit 3ae4be8662
15 changed files with 926 additions and 169 deletions
+39 -2
View File
@@ -37,6 +37,7 @@ use crate::{
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::dispatch::{DispatchError, DispatchResult};
use sp_core::crypto::UncheckedFrom;
use sp_runtime::RuntimeDebug;
use sp_sandbox::{SandboxEnvironmentBuilder, SandboxInstance, SandboxMemory};
use sp_std::prelude::*;
#[cfg(test)]
@@ -66,6 +67,10 @@ pub struct PrefabWasmModule<T: Config> {
maximum: u32,
/// Code instrumented with the latest schedule.
code: RelaxedCodeVec<T>,
/// A code that might contain non deterministic features and is therefore never allowed
/// to be run on chain. Specifically this code can never be instantiated into a contract
/// and can just be used through a delegate call.
determinism: Determinism,
/// The uninstrumented, pristine version of the code.
///
/// It is not stored because the pristine code has its own storage item. The value
@@ -102,6 +107,27 @@ pub struct OwnerInfo<T: Config> {
refcount: u64,
}
/// Defines the required determinism level of a wasm blob when either running or uploading code.
#[derive(
Clone, Copy, Encode, Decode, scale_info::TypeInfo, MaxEncodedLen, RuntimeDebug, PartialEq, Eq,
)]
pub enum Determinism {
/// The execution should be deterministic and hence no indeterministic instructions are
/// allowed.
///
/// Dispatchables always use this mode in order to make on-chain execution deterministic.
Deterministic,
/// Allow calling or uploading an indeterministic code.
///
/// This is only possible when calling into `pallet-contracts` directly via
/// [`crate::Pallet::bare_call`].
///
/// # Note
///
/// **Never** use this mode for on-chain execution.
AllowIndeterminism,
}
impl ExportedFunction {
/// The wasm export name for the function.
fn identifier(&self) -> &str {
@@ -124,11 +150,13 @@ where
original_code: Vec<u8>,
schedule: &Schedule<T>,
owner: AccountIdOf<T>,
determinism: Determinism,
) -> Result<Self, (DispatchError, &'static str)> {
let module = prepare::prepare_contract(
original_code.try_into().map_err(|_| (<Error<T>>::CodeTooLarge.into(), ""))?,
schedule,
owner,
determinism,
)?;
Ok(module)
}
@@ -258,6 +286,10 @@ where
fn code_len(&self) -> u32 {
self.code.len() as u32
}
fn is_deterministic(&self) -> bool {
matches!(self.determinism, Determinism::Deterministic)
}
}
#[cfg(test)]
@@ -551,8 +583,13 @@ mod tests {
fn execute<E: BorrowMut<MockExt>>(wat: &str, input_data: Vec<u8>, mut ext: E) -> ExecResult {
let wasm = wat::parse_str(wat).unwrap();
let schedule = crate::Schedule::default();
let executable =
PrefabWasmModule::<<MockExt as Ext>::T>::from_code(wasm, &schedule, ALICE).unwrap();
let executable = PrefabWasmModule::<<MockExt as Ext>::T>::from_code(
wasm,
&schedule,
ALICE,
Determinism::Deterministic,
)
.unwrap();
executable.execute(ext.borrow_mut(), &ExportedFunction::Call, input_data)
}