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 all 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
55 changes: 48 additions & 7 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.
not_deprecated: bool,
}

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 not_deprecated = 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,12 +233,22 @@ impl HostFn {
item.sig.ident.span(),
);
},
"deprecated" => {
if !not_deprecated {
return Err(err(span, "#[deprecated] can only be specified once"))
}
not_deprecated = false;
},
_ => return Err(err(span, msg)),
}
}
let name = item.sig.ident.to_string();

// process arguments: The first and second arg are treated differently (ctx, memory)
if !(is_stable || not_deprecated) {
return Err(err(span, "#[deprecated] is mutually exclusive with #[unstable]"))
}

// process arguments: The first and second args are treated differently (ctx, memory)
// they must exist and be `ctx: _` and `memory: _`.
let msg = "Every function must start with two inferred parameters: ctx: _ and memory: _";
let special_args = item
Expand Down Expand Up @@ -330,6 +343,7 @@ impl HostFn {
returns,
is_stable,
alias_to,
not_deprecated,
})
},
_ => 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 not_deprecated = f.not_deprecated;

// 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__) && (#not_deprecated || __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 mutually exclusive with `#[unstable]`.
/// Deprecated interfaces have the following properties:
/// - New contract codes utilizing those interfaces cannot be uploaded.
/// - New contracts from existing codes utilizing those interfaces cannot be instantiated.
/// - Existing contracts containing those interfaces still work.
///
/// 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 though random interface is deprecated, as the check to ban deprecated
/// 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 this deprecated functionality. It will
/// 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