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

contracts: Deprecate random interface #13204

Merged
merged 9 commits into from
Jan 25, 2023
Merged
Show file tree
Hide file tree
Changes from 4 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
53 changes: 47 additions & 6 deletions frame/contracts/proc-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ struct HostFn {
returns: HostFnReturn,
is_stable: bool,
alias_to: Option<String>,
/// Formulating the predicate inverted makes the expression using it simpler.
is_undeprecated: bool,
athei marked this conversation as resolved.
Show resolved Hide resolved
}

enum HostFnReturn {
Expand Down Expand Up @@ -199,13 +201,14 @@ impl HostFn {

// process attributes
let msg =
"only #[version(<u8>)], #[unstable] and #[prefixed_alias] attributes are allowed.";
"only #[version(<u8>)], #[unstable], #[prefixed_alias] and #[deprecated] attributes are allowed.";
let span = item.span();
let mut attrs = item.attrs.clone();
attrs.retain(|a| !a.path.is_ident("doc"));
let mut maybe_module = None;
let mut is_stable = true;
let mut alias_to = None;
let mut is_undeprecated = true;
while let Some(attr) = attrs.pop() {
let ident = attr.path.get_ident().ok_or(err(span, msg))?.to_string();
match ident.as_str() {
Expand All @@ -230,11 +233,21 @@ impl HostFn {
item.sig.ident.span(),
);
},
"deprecated" => {
if !is_undeprecated {
return Err(err(span, "#[deprecated] can only be specified once"))
}
is_undeprecated = false;
},
_ => return Err(err(span, msg)),
}
}
let name = item.sig.ident.to_string();

if !(is_stable || is_undeprecated) {
return Err(err(span, "#[deprecated] is exclusive with #[unstable]"))
athei marked this conversation as resolved.
Show resolved Hide resolved
}

// process arguments: The first and second arg are treated differently (ctx, memory)
athei marked this conversation as resolved.
Show resolved Hide resolved
// they must exist and be `ctx: _` and `memory: _`.
let msg = "Every function must start with two inferred parameters: ctx: _ and memory: _";
Expand Down Expand Up @@ -330,6 +343,7 @@ impl HostFn {
returns,
is_stable,
alias_to,
is_undeprecated,
})
},
_ => Err(err(span, &msg)),
Expand Down Expand Up @@ -510,15 +524,25 @@ fn expand_impls(def: &mut EnvDef) -> TokenStream2 {
quote! {
impl<'a, E: Ext> crate::wasm::Environment<crate::wasm::runtime::Runtime<'a, E>> for Env
{
fn define(store: &mut ::wasmi::Store<crate::wasm::Runtime<E>>, linker: &mut ::wasmi::Linker<crate::wasm::Runtime<E>>, allow_unstable: bool) -> Result<(), ::wasmi::errors::LinkerError> {
fn define(
store: &mut ::wasmi::Store<crate::wasm::Runtime<E>>,
linker: &mut ::wasmi::Linker<crate::wasm::Runtime<E>>,
allow_unstable: AllowUnstableInterface,
allow_deprecated: AllowDeprecatedInterface,
) -> Result<(),::wasmi::errors::LinkerError> {
#impls
Ok(())
}
}

impl crate::wasm::Environment<()> for Env
{
fn define(store: &mut ::wasmi::Store<()>, linker: &mut ::wasmi::Linker<()>, allow_unstable: bool) -> Result<(), ::wasmi::errors::LinkerError> {
fn define(
store: &mut ::wasmi::Store<()>,
linker: &mut ::wasmi::Linker<()>,
allow_unstable: AllowUnstableInterface,
allow_deprecated: AllowDeprecatedInterface,
) -> Result<(), ::wasmi::errors::LinkerError> {
#dummy_impls
Ok(())
}
Expand All @@ -542,6 +566,7 @@ fn expand_functions(
&f.item.sig.output
);
let is_stable = f.is_stable;
let is_undeprecated = f.is_undeprecated;

// If we don't expand blocks (implementing for `()`) we change a few things:
// - We replace any code by unreachable!
Expand Down Expand Up @@ -582,9 +607,13 @@ fn expand_functions(
};

quote! {
// We need to allow unstable functions when runtime benchmarks are performed because
// we generate the weights even when those interfaces are not enabled.
if ::core::cfg!(feature = "runtime-benchmarks") || #is_stable || allow_unstable {
// We need to allow all interfaces when runtime benchmarks are performed because
// we generate the weights even when those interfaces are not enabled. This
// is necessary as the decision whether we allow unstable or deprecated functions
// is a decision made at runtime. Generation of the weights happens statically.
if ::core::cfg!(feature = "runtime-benchmarks") ||
((#is_stable || __allow_unstable__) && (#is_undeprecated || __allow_deprecated__))
{
#allow_unused
linker.define(#module, #name, ::wasmi::Func::wrap(&mut*store, |mut __caller__: ::wasmi::Caller<#host_state>, #( #params, )*| -> #wasm_output {
let mut func = #inner;
Expand All @@ -596,6 +625,8 @@ fn expand_functions(
}
});
quote! {
let __allow_unstable__ = matches!(allow_unstable, AllowUnstableInterface::Yes);
let __allow_deprecated__ = matches!(allow_deprecated, AllowDeprecatedInterface::Yes);
#( #impls )*
}
}
Expand Down Expand Up @@ -691,6 +722,16 @@ fn expand_functions(
/// `...` modules each having its `Api` trait containing functions holding documentation for every
/// host function defined by the macro.
///
/// # Deprecated Interfaces
///
/// An interface can be annotated with `#[deprecated]`. It is exclusive with `#[unstable]`.
athei marked this conversation as resolved.
Show resolved Hide resolved
/// Deprecated interfaces have the following properties:
/// - New codes containing those interfaces cannot be uploaded.
athei marked this conversation as resolved.
Show resolved Hide resolved
/// - New contracts from existing coes containing those interfaces cannot be instantiated.
athei marked this conversation as resolved.
Show resolved Hide resolved
/// - Existing contracts containing those interfaces still work
athei marked this conversation as resolved.
Show resolved Hide resolved
///
/// Those interfaces will eventually be removed.
///
/// To build up these docs, run:
///
/// ```nocompile
Expand Down
13 changes: 11 additions & 2 deletions frame/contracts/src/benchmarking/sandbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
/// ! sandbox to execute the wasm code. This is because we do not need the full
/// ! environment that provides the seal interface as imported functions.
use super::{code::WasmModule, Config};
use crate::wasm::{Environment, PrefabWasmModule};
use crate::wasm::{
AllowDeprecatedInterface, AllowUnstableInterface, Environment, PrefabWasmModule,
};
use wasmi::{errors::LinkerError, Func, Linker, StackLimits, Store};

/// Minimal execution environment without any imported functions.
Expand Down Expand Up @@ -49,6 +51,8 @@ impl<T: Config> From<&WasmModule<T>> for Sandbox {
(),
memory,
StackLimits::default(),
// We are testing with an empty environment anyways
AllowDeprecatedInterface::No,
)
.expect("Failed to create benchmarking Sandbox instance");
let entry_point = instance.get_export(&store, "call").unwrap().into_func().unwrap();
Expand All @@ -59,7 +63,12 @@ impl<T: Config> From<&WasmModule<T>> for Sandbox {
struct EmptyEnv;

impl Environment<()> for EmptyEnv {
fn define(_: &mut Store<()>, _: &mut Linker<()>, _: bool) -> Result<(), LinkerError> {
fn define(
_: &mut Store<()>,
_: &mut Linker<()>,
_: AllowUnstableInterface,
_: AllowDeprecatedInterface,
) -> Result<(), LinkerError> {
Ok(())
}
}
31 changes: 31 additions & 0 deletions frame/contracts/src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3440,4 +3440,35 @@ mod tests {
));
});
}

/// This works even when randomness is disabled. The check to ban deprecated
athei marked this conversation as resolved.
Show resolved Hide resolved
/// functions happens in the wasm stack which is mocked for exec tests.
#[test]
fn randomness_works() {
let subject = b"nice subject".as_ref();
let code_hash = MockLoader::insert(Call, move |ctx, _| {
let rand = <Test as Config>::Randomness::random(subject);
assert_eq!(rand, ctx.ext.random(subject));
exec_success()
});

ExtBuilder::default().build().execute_with(|| {
let schedule = <Test as Config>::Schedule::get();
place_contract(&BOB, code_hash);

let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
let result = MockStack::run_call(
ALICE,
BOB,
&mut GasMeter::<Test>::new(GAS_LIMIT),
&mut storage_meter,
&schedule,
0,
vec![],
None,
Determinism::Deterministic,
);
assert_matches!(result, Ok(_));
});
}
}
9 changes: 8 additions & 1 deletion frame/contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,14 @@ pub mod pallet {
/// The time implementation used to supply timestamps to contracts through `seal_now`.
type Time: Time;

/// The generator used to supply randomness to contracts through `seal_random`
/// The generator used to supply randomness to contracts through `seal_random`.
///
/// # Deprecated
///
/// Codes using the randomness functionality cannot be uploaded. Neither can contracts
/// be instantiated from existing codes that use deprecated functionality. It will
athei marked this conversation as resolved.
Show resolved Hide resolved
/// be removed eventually. Hence for new `pallet-contracts` deployments it is okay
/// to supply a dummy implementation for this type (because it is never used).
type Randomness: Randomness<Self::Hash, Self::BlockNumber>;

/// The currency in which fees are paid and contract balances are held.
Expand Down
Loading