diff --git a/Cargo.lock b/Cargo.lock index 85520f21853e..bbdc4c7c88db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10367,7 +10367,6 @@ dependencies = [ "anyhow", "frame-system", "parity-wasm", - "polkavm-linker 0.9.2", "sp-runtime", "tempfile", "toml 0.8.12", @@ -10427,7 +10426,6 @@ dependencies = [ "bitflags 1.3.2", "parity-scale-codec", "paste", - "polkavm-derive 0.9.1", "scale-info", ] diff --git a/prdoc/pr_4851.prdoc b/prdoc/pr_4851.prdoc new file mode 100644 index 000000000000..923ca4bfff5d --- /dev/null +++ b/prdoc/pr_4851.prdoc @@ -0,0 +1,40 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Add support for deprecation metadata in `RuntimeMetadataIr` entries. + +doc: + - audience: + - Runtime dev + - Runtime user + description: | + Changes introduced are listed below. + Adds `DeprecationStatusIR` enum to sp_metadata_ir. + - Is a deprecation info for simple items. + Adds `DeprecationInfoIR` enum to sp_metadata_ir. + - It is a deprecation info for an enums/errors/calls. Contains `DeprecationStatusIR`. + Also denotes full/partial deprecation of the type or its variants/calls. + Adds `deprecation_info` field to + - `RuntimeApiMetadataIR` + - `RuntimeApiMethodMetadataIR` + - `StorageEntryMetadataIR` + - `PalletConstantMetadataIR` + - `PalletCallMetadataIR` + - `PalletMetadataIR` + - `PalletEventMetadataIR` + - `PalletErrorMetadataIR` + Examples of the deprecation info produced can be seen inside + - Tests for `frame-support` + - hackmd link https://hackmd.io/@Zett98/Bys0YgbcR + +crates: + - name: frame-support-procedural + bump: patch + - name: frame-support + bump: major + - name: sp-api-proc-macro + bump: patch + - name: sp-api + bump: patch + - name: sp-metadata-ir + bump: major diff --git a/prdoc/pr_5664.prdoc b/prdoc/pr_5664.prdoc new file mode 100644 index 000000000000..7fbe15006da3 --- /dev/null +++ b/prdoc/pr_5664.prdoc @@ -0,0 +1,11 @@ +title: Calling an address without associated code is a balance transfer + +doc: + - audience: Runtime Dev + description: | + This makes pallet_revive behave like EVM where a balance transfer + is just a call to a plain wallet. + +crates: + - name: pallet-revive + bump: patch diff --git a/prdoc/pr_5665.prdoc b/prdoc/pr_5665.prdoc new file mode 100644 index 000000000000..2b2f0547dc01 --- /dev/null +++ b/prdoc/pr_5665.prdoc @@ -0,0 +1,17 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "[pallet-contracts] remove riscv support" + +doc: + - audience: Runtime Dev + description: | + RISC-V support is now being built inside the new fork pallet-revive + +crates: + - name: pallet-contracts + bump: patch + - name: pallet-contracts-fixtures + bump: patch + - name: pallet-contracts-uapi + bump: patch diff --git a/prdoc/pr_5678.prdoc b/prdoc/pr_5678.prdoc new file mode 100644 index 000000000000..af1fac31c560 --- /dev/null +++ b/prdoc/pr_5678.prdoc @@ -0,0 +1,14 @@ +title: 'rpc server: fix deny unsafe on RpcMethods::Auto' +doc: +- audience: Node User + description: |- + Close #5677 + + I made a nit when I moved this code: https://github.com/paritytech/polkadot-sdk/blob/v1.14.0-rc1/substrate/client/service/src/lib.rs#L379-#L385 in https://github.com/paritytech/polkadot-sdk/pull/4792 + + Thus: + - (ip.is_loopback(), RpcMethods::Auto) -> allow unsafe + - (!ip.is_loopback(), RpcMethods::Auto) -> deny unsafe +crates: +- name: sc-rpc-server + bump: patch diff --git a/substrate/client/rpc-servers/src/utils.rs b/substrate/client/rpc-servers/src/utils.rs index 5b4a4bf22b95..d9b2db7af133 100644 --- a/substrate/client/rpc-servers/src/utils.rs +++ b/substrate/client/rpc-servers/src/utils.rs @@ -284,7 +284,7 @@ pub(crate) fn get_proxy_ip(req: &http::Request) -> Option { /// Get the `deny_unsafe` setting based on the address and the RPC methods exposed by the interface. pub fn deny_unsafe(addr: &SocketAddr, methods: &RpcMethods) -> DenyUnsafe { match (addr.ip().is_loopback(), methods) { - | (_, RpcMethods::Unsafe) | (false, RpcMethods::Auto) => DenyUnsafe::No, + (_, RpcMethods::Unsafe) | (true, RpcMethods::Auto) => DenyUnsafe::No, _ => DenyUnsafe::Yes, } } diff --git a/substrate/frame/contracts/fixtures/Cargo.toml b/substrate/frame/contracts/fixtures/Cargo.toml index 6cb6447d8fd7..4c01c1f061b7 100644 --- a/substrate/frame/contracts/fixtures/Cargo.toml +++ b/substrate/frame/contracts/fixtures/Cargo.toml @@ -20,8 +20,4 @@ parity-wasm = { workspace = true } tempfile = { workspace = true } toml = { workspace = true } twox-hash = { workspace = true, default-features = true } -polkavm-linker = { workspace = true, optional = true } anyhow = { workspace = true, default-features = true } - -[features] -riscv = ["polkavm-linker"] diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index baaeaf034203..7af733c536d0 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Compile contracts to wasm and RISC-V binaries. +//! Compile contracts to wasm. use anyhow::{bail, Context, Result}; use parity_wasm::elements::{deserialize_file, serialize_to_file, Internal}; use std::{ @@ -89,12 +89,6 @@ impl Entry { fn out_wasm_filename(&self) -> String { format!("{}.wasm", self.name()) } - - /// Return the name of the RISC-V polkavm file. - #[cfg(feature = "riscv")] - fn out_riscv_filename(&self) -> String { - format!("{}.polkavm", self.name()) - } } /// Collect all contract entries from the given source directory. @@ -236,53 +230,6 @@ fn post_process_wasm(input_path: &Path, output_path: &Path) -> Result<()> { serialize_to_file(output_path, module).map_err(Into::into) } -/// Build contracts for RISC-V. -#[cfg(feature = "riscv")] -fn invoke_riscv_build(current_dir: &Path) -> Result<()> { - let encoded_rustflags = [ - "-Crelocation-model=pie", - "-Clink-arg=--emit-relocs", - "-Clink-arg=--export-dynamic-symbol=__polkavm_symbol_export_hack__*", - ] - .join("\x1f"); - - let build_res = Command::new(env::var("CARGO")?) - .current_dir(current_dir) - .env_clear() - .env("PATH", env::var("PATH").unwrap_or_default()) - .env("CARGO_ENCODED_RUSTFLAGS", encoded_rustflags) - .env("RUSTUP_TOOLCHAIN", "rve-nightly") - .env("RUSTUP_HOME", env::var("RUSTUP_HOME").unwrap_or_default()) - .args(["build", "--release", "--target=riscv32ema-unknown-none-elf"]) - .output() - .expect("failed to execute process"); - - if build_res.status.success() { - return Ok(()) - } - - let stderr = String::from_utf8_lossy(&build_res.stderr); - - if stderr.contains("'rve-nightly' is not installed") { - eprintln!("RISC-V toolchain is not installed.\nDownload and install toolchain from https://github.com/paritytech/rustc-rv32e-toolchain."); - eprintln!("{}", stderr); - } else { - eprintln!("{}", stderr); - } - - bail!("Failed to build contracts"); -} -/// Post-process the compiled wasm contracts. -#[cfg(feature = "riscv")] -fn post_process_riscv(input_path: &Path, output_path: &Path) -> Result<()> { - let mut config = polkavm_linker::Config::default(); - config.set_strip(true); - let orig = fs::read(input_path).with_context(|| format!("Failed to read {:?}", input_path))?; - let linked = polkavm_linker::program_from_elf(config, orig.as_ref()) - .map_err(|err| anyhow::format_err!("Failed to link polkavm program: {}", err))?; - fs::write(output_path, linked.as_bytes()).map_err(Into::into) -} - /// Write the compiled contracts to the given output directory. fn write_output(build_dir: &Path, out_dir: &Path, entries: Vec) -> Result<()> { for entry in entries { @@ -292,12 +239,6 @@ fn write_output(build_dir: &Path, out_dir: &Path, entries: Vec) -> Result &out_dir.join(&wasm_output), )?; - #[cfg(feature = "riscv")] - post_process_riscv( - &build_dir.join("target/riscv32ema-unknown-none-elf/release").join(entry.name()), - &out_dir.join(entry.out_riscv_filename()), - )?; - entry.update_cache(out_dir)?; } @@ -347,9 +288,6 @@ fn main() -> Result<()> { invoke_wasm_build(tmp_dir_path)?; - #[cfg(feature = "riscv")] - invoke_riscv_build(tmp_dir_path)?; - write_output(tmp_dir_path, &out_dir, entries)?; Ok(()) } diff --git a/substrate/frame/contracts/fixtures/contracts/common/src/lib.rs b/substrate/frame/contracts/fixtures/contracts/common/src/lib.rs index 80e1f543a7b5..504fa7f9e81d 100644 --- a/substrate/frame/contracts/fixtures/contracts/common/src/lib.rs +++ b/substrate/frame/contracts/fixtures/contracts/common/src/lib.rs @@ -22,13 +22,6 @@ pub use uapi::{HostFn, HostFnImpl as api}; fn panic(_info: &core::panic::PanicInfo) -> ! { #[cfg(target_arch = "wasm32")] core::arch::wasm32::unreachable(); - - #[cfg(target_arch = "riscv32")] - // Safety: The unimp instruction is guaranteed to trap - unsafe { - core::arch::asm!("unimp"); - core::hint::unreachable_unchecked(); - } } /// Utility macro to read input passed to a contract. diff --git a/substrate/frame/contracts/fixtures/src/lib.rs b/substrate/frame/contracts/fixtures/src/lib.rs index 56a8e2321c5c..f9272a32ad61 100644 --- a/substrate/frame/contracts/fixtures/src/lib.rs +++ b/substrate/frame/contracts/fixtures/src/lib.rs @@ -39,7 +39,5 @@ mod test { fn out_dir_should_have_compiled_mocks() { let out_dir: std::path::PathBuf = env!("OUT_DIR").into(); assert!(out_dir.join("dummy.wasm").exists()); - #[cfg(feature = "riscv")] - assert!(out_dir.join("dummy.polkavm").exists()); } } diff --git a/substrate/frame/contracts/uapi/Cargo.toml b/substrate/frame/contracts/uapi/Cargo.toml index ccd86ba7890c..09c70c287899 100644 --- a/substrate/frame/contracts/uapi/Cargo.toml +++ b/substrate/frame/contracts/uapi/Cargo.toml @@ -20,9 +20,6 @@ codec = { features = [ "max-encoded-len", ], optional = true, workspace = true } -[target.'cfg(target_arch = "riscv32")'.dependencies] -polkavm-derive = { workspace = true } - [package.metadata.docs.rs] default-target = ["wasm32-unknown-unknown"] diff --git a/substrate/frame/contracts/uapi/src/host.rs b/substrate/frame/contracts/uapi/src/host.rs index 51f0cd7eb2dc..52c9ea387e1c 100644 --- a/substrate/frame/contracts/uapi/src/host.rs +++ b/substrate/frame/contracts/uapi/src/host.rs @@ -17,9 +17,6 @@ use paste::paste; #[cfg(target_arch = "wasm32")] mod wasm32; -#[cfg(target_arch = "riscv32")] -mod riscv32; - macro_rules! hash_fn { ( $name:ident, $bytes:literal ) => { paste! { @@ -66,7 +63,7 @@ fn ptr_or_sentinel(data: &Option<&[u8]>) -> *const u8 { /// Implements [`HostFn`] for each supported target architecture. pub enum HostFnImpl {} -/// Defines all the host apis implemented by both wasm and RISC-V vms. +/// Defines all the host apis implemented by the wasm vm. pub trait HostFn: private::Sealed { /// Returns the number of times specified contract exists on the call stack. Delegated calls are /// not counted as separate calls. diff --git a/substrate/frame/contracts/uapi/src/host/riscv32.rs b/substrate/frame/contracts/uapi/src/host/riscv32.rs deleted file mode 100644 index 355520233212..000000000000 --- a/substrate/frame/contracts/uapi/src/host/riscv32.rs +++ /dev/null @@ -1,327 +0,0 @@ -#![allow(unused_variables, unused_mut)] -// Copyright (C) Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// TODO: bring up to date with wasm32.rs - -use super::{CallFlags, HostFn, HostFnImpl, Result}; -use crate::ReturnFlags; - -/// A macro to implement all Host functions with a signature of `fn(&mut &mut [u8])`. -/// -/// Example: -/// ```nocompile -// impl_wrapper_for! { -// () => [gas_left], -// (v1) => [gas_left], -// } -// ``` -// -// Expands to: -// ```nocompile -// fn gas_left(output: &mut &mut [u8]) { -// unsafe { sys::gas_left(...); } -// } -// fn gas_left_v1(output: &mut &mut [u8]) { -// unsafe { sys::v1::gas_left(...); } -// } -// ``` -macro_rules! impl_wrapper_for { - (@impl_fn $( $mod:ident )::*, $suffix_sep: literal, $suffix:tt, $name:ident) => { - paste::paste! { - fn [<$name $suffix_sep $suffix>](output: &mut &mut [u8]) { - todo!() - } - } - }; - - () => {}; - - (($mod:ident) => [$( $name:ident),*], $($tail:tt)*) => { - $(impl_wrapper_for!(@impl_fn sys::$mod, "_", $mod, $name);)* - impl_wrapper_for!($($tail)*); - }; - - (() => [$( $name:ident),*], $($tail:tt)*) => { - $(impl_wrapper_for!(@impl_fn sys, "", "", $name);)* - impl_wrapper_for!($($tail)*); - }; -} - -/// A macro to implement all the hash functions Apis. -macro_rules! impl_hash_fn { - ( $name:ident, $bytes_result:literal ) => { - paste::item! { - fn [](input: &[u8], output: &mut [u8; $bytes_result]) { - todo!() - } - } - }; -} - -/// A macro to implement the get_storage functions. -macro_rules! impl_get_storage { - ($fn_name:ident, $sys_get_storage:path) => { - fn $fn_name(key: &[u8], output: &mut &mut [u8]) -> Result { - todo!() - } - }; -} - -impl HostFn for HostFnImpl { - fn instantiate_v1( - code_hash: &[u8], - gas: u64, - value: &[u8], - input: &[u8], - mut address: Option<&mut &mut [u8]>, - mut output: Option<&mut &mut [u8]>, - salt: &[u8], - ) -> Result { - todo!() - } - - fn instantiate_v2( - code_hash: &[u8], - ref_time_limit: u64, - proof_size_limit: u64, - deposit: Option<&[u8]>, - value: &[u8], - input: &[u8], - mut address: Option<&mut &mut [u8]>, - mut output: Option<&mut &mut [u8]>, - salt: &[u8], - ) -> Result { - todo!() - } - - fn call( - callee: &[u8], - gas: u64, - value: &[u8], - input_data: &[u8], - mut output: Option<&mut &mut [u8]>, - ) -> Result { - todo!() - } - - fn call_v1( - flags: CallFlags, - callee: &[u8], - gas: u64, - value: &[u8], - input_data: &[u8], - mut output: Option<&mut &mut [u8]>, - ) -> Result { - todo!() - } - - fn call_v2( - flags: CallFlags, - callee: &[u8], - ref_time_limit: u64, - proof_size_limit: u64, - deposit: Option<&[u8]>, - value: &[u8], - input_data: &[u8], - mut output: Option<&mut &mut [u8]>, - ) -> Result { - todo!() - } - - fn caller_is_root() -> u32 { - todo!() - } - - fn delegate_call( - flags: CallFlags, - code_hash: &[u8], - input: &[u8], - mut output: Option<&mut &mut [u8]>, - ) -> Result { - todo!() - } - - fn transfer(account_id: &[u8], value: &[u8]) -> Result { - todo!() - } - - fn deposit_event(topics: &[u8], data: &[u8]) { - todo!() - } - - fn set_storage(key: &[u8], value: &[u8]) { - todo!() - } - - fn set_storage_v1(key: &[u8], encoded_value: &[u8]) -> Option { - todo!() - } - - fn set_storage_v2(key: &[u8], encoded_value: &[u8]) -> Option { - todo!() - } - - fn set_transient_storage(key: &[u8], encoded_value: &[u8]) -> Option { - todo!() - } - - fn clear_storage(key: &[u8]) { - todo!() - } - - fn clear_storage_v1(key: &[u8]) -> Option { - todo!() - } - - fn clear_transient_storage(key: &[u8]) -> Option { - todo!() - } - - impl_get_storage!(get_storage, sys::get_storage); - impl_get_storage!(get_storage_v1, sys::v1::get_storage); - - fn get_transient_storage(key: &[u8], output: &mut &mut [u8]) -> Result { - todo!() - } - - fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result { - todo!() - } - - fn take_transient_storage(key: &[u8], output: &mut &mut [u8]) -> Result { - todo!() - } - - fn contains_storage(key: &[u8]) -> Option { - todo!() - } - - fn contains_storage_v1(key: &[u8]) -> Option { - todo!() - } - - fn contains_transient_storage(key: &[u8]) -> Option { - todo!() - } - - fn terminate(beneficiary: &[u8]) -> ! { - todo!() - } - - fn terminate_v1(beneficiary: &[u8]) -> ! { - todo!() - } - - fn call_chain_extension(func_id: u32, input: &[u8], output: Option<&mut &mut [u8]>) -> u32 { - todo!() - } - - fn input(output: &mut &mut [u8]) { - todo!() - } - - fn return_value(flags: ReturnFlags, return_value: &[u8]) -> ! { - todo!() - } - - fn call_runtime(call: &[u8]) -> Result { - todo!() - } - - fn debug_message(str: &[u8]) -> Result { - todo!() - } - - impl_wrapper_for! { - () => [caller, block_number, address, balance, gas_left, value_transferred, now, minimum_balance], - (v1) => [gas_left], - } - - fn weight_to_fee(gas: u64, output: &mut &mut [u8]) { - todo!() - } - - fn weight_to_fee_v1(ref_time_limit: u64, proof_size_limit: u64, output: &mut &mut [u8]) { - todo!() - } - - impl_hash_fn!(sha2_256, 32); - impl_hash_fn!(keccak_256, 32); - impl_hash_fn!(blake2_256, 32); - impl_hash_fn!(blake2_128, 16); - - fn ecdsa_recover( - signature: &[u8; 65], - message_hash: &[u8; 32], - output: &mut [u8; 33], - ) -> Result { - todo!() - } - - fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result { - todo!() - } - - fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result { - todo!() - } - - fn is_contract(account_id: &[u8]) -> bool { - todo!() - } - - fn caller_is_origin() -> bool { - todo!() - } - - fn set_code_hash(code_hash: &[u8]) -> Result { - todo!() - } - - fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result { - todo!() - } - - fn own_code_hash(output: &mut [u8]) { - todo!() - } - - fn account_reentrance_count(account: &[u8]) -> u32 { - todo!() - } - - fn lock_delegate_dependency(code_hash: &[u8]) { - todo!() - } - - fn unlock_delegate_dependency(code_hash: &[u8]) { - todo!() - } - - fn instantiation_nonce() -> u64 { - todo!() - } - - fn reentrance_count() -> u32 { - todo!() - } - - fn xcm_execute(msg: &[u8]) -> Result { - todo!() - } - - fn xcm_send(dest: &[u8], msg: &[u8], output: &mut [u8; 32]) -> Result { - todo!() - } -} diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index 83877c6efd40..7e30c811ee07 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -21,10 +21,10 @@ mod flags; pub use flags::*; -#[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] +#[cfg(target_arch = "wasm32")] mod host; -#[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] +#[cfg(target_arch = "wasm32")] pub use host::*; macro_rules! define_error_codes { diff --git a/substrate/frame/revive/fixtures/contracts/call_return_code.rs b/substrate/frame/revive/fixtures/contracts/call_return_code.rs index 25370459acb4..d0d7c1bee2a5 100644 --- a/substrate/frame/revive/fixtures/contracts/call_return_code.rs +++ b/substrate/frame/revive/fixtures/contracts/call_return_code.rs @@ -34,6 +34,7 @@ pub extern "C" fn call() { input!( 100, callee_addr: &[u8; 20], + value: &[u8; 32], input: [u8], ); @@ -44,7 +45,7 @@ pub extern "C" fn call() { 0u64, // How much ref_time to devote for the execution. 0 = all. 0u64, // How much proof_size to devote for the execution. 0 = all. None, // No deposit limit. - &u256_bytes(100u64), // Value transferred to the contract. + value, // Value transferred to the contract. input, None, ) { diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs index 468f5aa8240e..587d89b31984 100644 --- a/substrate/frame/revive/src/exec.rs +++ b/substrate/frame/revive/src/exec.rs @@ -64,6 +64,8 @@ pub type ExecResult = Result; /// Type for variable sized storage key. Used for transparent hashing. type VarSizedKey = BoundedVec>; +const FRAME_ALWAYS_EXISTS_ON_INSTANTIATE: &str = "The return value is only `None` if no contract exists at the specified address. This cannot happen on instantiate or delegate; qed"; + /// Combined key type for both fixed and variable sized storage keys. pub enum Key { /// Variant for fixed sized keys. @@ -591,26 +593,6 @@ enum CachedContract { Terminated, } -impl CachedContract { - /// Return `Some(ContractInfo)` if the contract is in cached state. `None` otherwise. - fn into_contract(self) -> Option> { - if let CachedContract::Cached(contract) = self { - Some(contract) - } else { - None - } - } - - /// Return `Some(&mut ContractInfo)` if the contract is in cached state. `None` otherwise. - fn as_contract(&mut self) -> Option<&mut ContractInfo> { - if let CachedContract::Cached(contract) = self { - Some(contract) - } else { - None - } - } -} - impl Frame { /// Return the `contract_info` of the current contract. fn contract_info(&mut self) -> &mut ContractInfo { @@ -668,6 +650,24 @@ macro_rules! top_frame_mut { } impl CachedContract { + /// Return `Some(ContractInfo)` if the contract is in cached state. `None` otherwise. + fn into_contract(self) -> Option> { + if let CachedContract::Cached(contract) = self { + Some(contract) + } else { + None + } + } + + /// Return `Some(&mut ContractInfo)` if the contract is in cached state. `None` otherwise. + fn as_contract(&mut self) -> Option<&mut ContractInfo> { + if let CachedContract::Cached(contract) = self { + Some(contract) + } else { + None + } + } + /// Load the `contract_info` from storage if necessary. fn load(&mut self, account_id: &T::AccountId) { if let CachedContract::Invalidated = self { @@ -717,20 +717,20 @@ where value: BalanceOf, input_data: Vec, debug_message: Option<&'a mut DebugBuffer>, - ) -> Result { - let (mut stack, executable) = Self::new( - FrameArgs::Call { - dest: T::AddressMapper::to_account_id(&dest), - cached_info: None, - delegated_call: None, - }, - origin, + ) -> ExecResult { + let dest = T::AddressMapper::to_account_id(&dest); + if let Some((mut stack, executable)) = Self::new( + FrameArgs::Call { dest: dest.clone(), cached_info: None, delegated_call: None }, + origin.clone(), gas_meter, storage_meter, value, debug_message, - )?; - stack.run(executable, input_data) + )? { + stack.run(executable, input_data) + } else { + Self::transfer_no_contract(&origin, &dest, value) + } } /// Create and run a new call stack by instantiating a new contract. @@ -765,7 +765,8 @@ where storage_meter, value, debug_message, - )?; + )? + .expect(FRAME_ALWAYS_EXISTS_ON_INSTANTIATE); let address = T::AddressMapper::to_address(&stack.top_frame().account_id); stack.run(executable, input_data).map(|ret| (address, ret)) } @@ -792,9 +793,13 @@ where debug_message, ) .unwrap() + .unwrap() } /// Create a new call stack. + /// + /// Returns `None` when calling a non existant contract. This is not an error case + /// since this will result in a value transfer. fn new( args: FrameArgs, origin: Origin, @@ -802,8 +807,8 @@ where storage_meter: &'a mut storage::meter::Meter, value: BalanceOf, debug_message: Option<&'a mut DebugBuffer>, - ) -> Result<(Self, E), ExecError> { - let (first_frame, executable) = Self::new_frame( + ) -> Result, ExecError> { + let Some((first_frame, executable)) = Self::new_frame( args, value, gas_meter, @@ -811,7 +816,10 @@ where storage_meter, BalanceOf::::zero(), false, - )?; + )? + else { + return Ok(None); + }; let stack = Self { origin, @@ -826,7 +834,7 @@ where _phantom: Default::default(), }; - Ok((stack, executable)) + Ok(Some((stack, executable))) } /// Construct a new frame. @@ -841,15 +849,20 @@ where storage_meter: &mut storage::meter::GenericMeter, deposit_limit: BalanceOf, read_only: bool, - ) -> Result<(Frame, E), ExecError> { + ) -> Result, E)>, ExecError> { let (account_id, contract_info, executable, delegate_caller, entry_point) = match frame_args { FrameArgs::Call { dest, cached_info, delegated_call } => { let contract = if let Some(contract) = cached_info { contract } else { - >::get(T::AddressMapper::to_address(&dest)) - .ok_or(>::ContractNotFound)? + if let Some(contract) = + >::get(T::AddressMapper::to_address(&dest)) + { + contract + } else { + return Ok(None) + } }; let (executable, delegate_caller) = @@ -896,7 +909,7 @@ where read_only, }; - Ok((frame, executable)) + Ok(Some((frame, executable))) } /// Create a subsequent nested frame. @@ -907,7 +920,7 @@ where gas_limit: Weight, deposit_limit: BalanceOf, read_only: bool, - ) -> Result { + ) -> Result, ExecError> { if self.frames.len() as u32 == limits::CALL_STACK_DEPTH { return Err(Error::::MaxCallDepthReached.into()); } @@ -929,7 +942,7 @@ where let frame = top_frame_mut!(self); let nested_gas = &mut frame.nested_gas; let nested_storage = &mut frame.nested_storage; - let (frame, executable) = Self::new_frame( + if let Some((frame, executable)) = Self::new_frame( frame_args, value_transferred, nested_gas, @@ -937,15 +950,18 @@ where nested_storage, deposit_limit, read_only, - )?; - self.frames.try_push(frame).map_err(|_| Error::::MaxCallDepthReached)?; - Ok(executable) + )? { + self.frames.try_push(frame).map_err(|_| Error::::MaxCallDepthReached)?; + Ok(Some(executable)) + } else { + Ok(None) + } } /// Run the current (top) frame. /// /// This can be either a call or an instantiate. - fn run(&mut self, executable: E, input_data: Vec) -> Result { + fn run(&mut self, executable: E, input_data: Vec) -> ExecResult { let frame = self.top_frame(); let entry_point = frame.entry_point; let delegated_code_hash = @@ -954,13 +970,15 @@ where self.transient_storage.start_transaction(); let do_transaction = || { + let caller = self.caller(); + let frame = top_frame_mut!(self); + // We need to charge the storage deposit before the initial transfer so that // it can create the account in case the initial transfer is < ed. if entry_point == ExportedFunction::Constructor { // Root origin can't be used to instantiate a contract, so it is safe to assume that // if we reached this point the origin has an associated account. let origin = &self.origin.account_id()?; - let frame = top_frame_mut!(self); frame.nested_storage.charge_instantiate( origin, &frame.account_id, @@ -969,11 +987,15 @@ where )?; // Needs to be incremented before calling into the code so that it is visible // in case of recursion. - >::inc_account_nonce(self.caller().account_id()?); + >::inc_account_nonce(caller.account_id()?); } // Every non delegate call or instantiate also optionally transfers the balance. - self.initial_transfer()?; + // If it is a delegate call, then we've already transferred tokens in the + // last non-delegate frame. + if delegated_code_hash.is_none() { + Self::transfer_from_origin(&caller, &frame.account_id, frame.value_transferred)?; + } let contract_address = T::AddressMapper::to_address(&top_frame!(self).account_id); @@ -1166,40 +1188,40 @@ where } /// Transfer some funds from `from` to `to`. - fn transfer( - preservation: Preservation, - from: &T::AccountId, - to: &T::AccountId, - value: BalanceOf, - ) -> DispatchResult { - if !value.is_zero() && from != to { - T::Currency::transfer(from, to, value, preservation) + fn transfer(from: &T::AccountId, to: &T::AccountId, value: BalanceOf) -> DispatchResult { + // this avoids events to be emitted for zero balance transfers + if !value.is_zero() { + T::Currency::transfer(from, to, value, Preservation::Preserve) .map_err(|_| Error::::TransferFailed)?; } Ok(()) } - // The transfer as performed by a call or instantiate. - fn initial_transfer(&self) -> DispatchResult { - let frame = self.top_frame(); - - // If it is a delegate call, then we've already transferred tokens in the - // last non-delegate frame. - if frame.delegate_caller.is_some() { - return Ok(()); - } - - let value = frame.value_transferred; - - // Get the account id from the caller. - // If the caller is root there is no account to transfer from, and therefore we can't take - // any `value` other than 0. - let caller = match self.caller() { + /// Same as `transfer` but `from` is an `Origin`. + fn transfer_from_origin( + from: &Origin, + to: &T::AccountId, + value: BalanceOf, + ) -> DispatchResult { + // If the from address is root there is no account to transfer from, and therefore we can't + // take any `value` other than 0. + let from = match from { Origin::Signed(caller) => caller, Origin::Root if value.is_zero() => return Ok(()), Origin::Root => return DispatchError::RootNotAllowed.into(), }; - Self::transfer(Preservation::Preserve, &caller, &frame.account_id, value) + Self::transfer(from, to, value) + } + + /// Same as `transfer_from_origin` but creates an `ExecReturnValue` on success. + fn transfer_no_contract( + from: &Origin, + to: &T::AccountId, + value: BalanceOf, + ) -> ExecResult { + Self::transfer_from_origin(from, to, value) + .map(|_| ExecReturnValue::default()) + .map_err(Into::into) } /// Reference to the current (top) frame. @@ -1255,13 +1277,14 @@ where input_data: Vec, allows_reentry: bool, read_only: bool, - ) -> Result { + ) -> ExecResult { // Before pushing the new frame: Protect the caller contract against reentrancy attacks. // It is important to do this before calling `allows_reentry` so that a direct recursion // is caught by it. self.top_frame_mut().allows_reentry = allows_reentry; let dest = T::AddressMapper::to_account_id(dest); + let value = value.try_into().map_err(|_| Error::::BalanceConversionFailed)?; let try_call = || { if !self.allows_reentry(&dest) { @@ -1278,15 +1301,22 @@ where CachedContract::Cached(contract) => Some(contract.clone()), _ => None, }); - let executable = self.push_frame( - FrameArgs::Call { dest, cached_info, delegated_call: None }, - value.try_into().map_err(|_| Error::::BalanceConversionFailed)?, + if let Some(executable) = self.push_frame( + FrameArgs::Call { dest: dest.clone(), cached_info, delegated_call: None }, + value, gas_limit, deposit_limit.try_into().map_err(|_| Error::::BalanceConversionFailed)?, // Enable read-only access if requested; cannot disable it if already set. read_only || self.is_read_only(), - )?; - self.run(executable, input_data) + )? { + self.run(executable, input_data) + } else { + Self::transfer_no_contract( + &Origin::from_account_id(self.account_id().clone()), + &dest, + value, + ) + } }; // We need to make sure to reset `allows_reentry` even on failure. @@ -1319,7 +1349,7 @@ where BalanceOf::::zero(), self.is_read_only(), )?; - self.run(executable, input_data) + self.run(executable.expect(FRAME_ALWAYS_EXISTS_ON_INSTANTIATE), input_data) } fn instantiate( @@ -1346,7 +1376,8 @@ where self.is_read_only(), )?; let address = T::AddressMapper::to_address(&self.top_frame().account_id); - self.run(executable, input_data).map(|ret| (address, ret)) + self.run(executable.expect(FRAME_ALWAYS_EXISTS_ON_INSTANTIATE), input_data) + .map(|ret| (address, ret)) } fn terminate(&mut self, beneficiary: &H160) -> DispatchResult { @@ -1379,7 +1410,6 @@ where fn transfer(&mut self, to: &H160, value: U256) -> DispatchResult { Self::transfer( - Preservation::Preserve, &self.top_frame().account_id, &T::AddressMapper::to_account_id(to), value.try_into().map_err(|_| Error::::BalanceConversionFailed)?, @@ -1854,7 +1884,7 @@ mod tests { set_balance(&ALICE, 100); set_balance(&BOB, 0); - MockStack::transfer(Preservation::Preserve, &ALICE, &BOB, 55).unwrap(); + MockStack::transfer(&ALICE, &BOB, 55).unwrap(); assert_eq!(get_balance(&ALICE), 45); assert_eq!(get_balance(&BOB), 55); @@ -1974,7 +2004,7 @@ mod tests { ExtBuilder::default().build().execute_with(|| { set_balance(&origin, 0); - let result = MockStack::transfer(Preservation::Preserve, &origin, &dest, 100); + let result = MockStack::transfer(&origin, &dest, 100); assert_eq!(result, Err(Error::::TransferFailed.into())); assert_eq!(get_balance(&origin), 0); @@ -2848,15 +2878,35 @@ mod tests { } #[test] - fn recursive_call_during_constructor_fails() { + fn recursive_call_during_constructor_is_balance_transfer() { let code = MockLoader::insert(Constructor, |ctx, _| { let account_id = ctx.ext.account_id().clone(); let addr = ::AddressMapper::to_address(&account_id); + let balance = ctx.ext.balance(); - assert_matches!( - ctx.ext.call(Weight::zero(), U256::zero(), &addr, U256::zero(), vec![], - true, false), Err(ExecError{error, ..}) if error == >::ContractNotFound.into() - ); + // Calling ourselves during the constructor will trigger a balance + // transfer since no contract exist yet. + assert_ok!(ctx.ext.call( + Weight::zero(), + U256::zero(), + &addr, + (balance - 1).into(), + vec![], + true, + false + )); + + // Should also work with call data set as it is ignored when no + // contract is deployed. + assert_ok!(ctx.ext.call( + Weight::zero(), + U256::zero(), + &addr, + 1u32.into(), + vec![1, 2, 3, 4], + true, + false + )); exec_success() }); @@ -2879,7 +2929,7 @@ mod tests { executable, &mut gas_meter, &mut storage_meter, - min_balance, + 10, vec![], Some(&[0; 32]), None, @@ -2888,6 +2938,51 @@ mod tests { }); } + #[test] + fn cannot_send_more_balance_than_available_to_self() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + let account_id = ctx.ext.account_id().clone(); + let addr = ::AddressMapper::to_address(&account_id); + let balance = ctx.ext.balance(); + + assert_err!( + ctx.ext.call( + Weight::zero(), + U256::zero(), + &addr, + (balance + 1).into(), + vec![], + true, + false + ), + >::TransferFailed + ); + exec_success() + }); + + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .build() + .execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 10); + place_contract(&BOB, code_hash); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); + MockStack::run_call( + origin, + BOB_ADDR, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + None, + ) + .unwrap(); + }); + } + #[test] fn printing_works() { let code_hash = MockLoader::insert(Call, |ctx, _| { diff --git a/substrate/frame/revive/src/primitives.rs b/substrate/frame/revive/src/primitives.rs index 1b48527d23d7..67bc144c3dd2 100644 --- a/substrate/frame/revive/src/primitives.rs +++ b/substrate/frame/revive/src/primitives.rs @@ -106,7 +106,7 @@ pub enum ContractAccessError { } /// Output of a contract call or instantiation which ran to completion. -#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, Default)] pub struct ExecReturnValue { /// Flags passed along by `seal_return`. Empty when `seal_return` was never called. pub flags: ReturnFlags, diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index 73914c9aae07..19d6eabd577b 100644 --- a/substrate/frame/revive/src/tests.rs +++ b/substrate/frame/revive/src/tests.rs @@ -50,7 +50,6 @@ use codec::{Decode, Encode}; use frame_support::{ assert_err, assert_err_ignore_postinfo, assert_err_with_weight, assert_noop, assert_ok, derive_impl, - dispatch::{DispatchErrorWithPostInfo, PostDispatchInfo}, pallet_prelude::EnsureOrigin, parameter_types, storage::child, @@ -159,6 +158,12 @@ pub mod test_utils { // Assert that contract code is stored, and get its size. PristineCode::::try_get(&code_hash).unwrap().len() } + pub fn u256_bytes(u: u64) -> [u8; 32] { + let mut buffer = [0u8; 32]; + let bytes = u.to_le_bytes(); + buffer[..8].copy_from_slice(&bytes); + buffer + } } mod builder { @@ -589,25 +594,15 @@ mod run_tests { use pretty_assertions::{assert_eq, assert_ne}; use sp_core::U256; - // Perform a call to a plain account. - // The actual transfer fails because we can only call contracts. - // Then we check that at least the base costs where charged (no runtime gas costs.) #[test] - fn calling_plain_account_fails() { + fn calling_plain_account_is_balance_transfer() { ExtBuilder::default().build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 100_000_000); - let base_cost = <::WeightInfo as WeightInfo>::call(); - - assert_eq!( - builder::call(BOB_ADDR).build(), - Err(DispatchErrorWithPostInfo { - error: Error::::ContractNotFound.into(), - post_info: PostDispatchInfo { - actual_weight: Some(base_cost), - pays_fee: Default::default(), - }, - }) - ); + assert!(!>::contains_key(BOB_ADDR)); + assert_eq!(test_utils::get_balance(&BOB_CONTRACT_ID), 0); + let result = builder::bare_call(BOB_ADDR).value(42).build_and_unwrap_result(); + assert_eq!(test_utils::get_balance(&BOB_CONTRACT_ID), 42); + assert_eq!(result, Default::default()); }); } @@ -1472,6 +1467,8 @@ mod run_tests { #[test] fn call_return_code() { + use test_utils::u256_bytes; + let (caller_code, _caller_hash) = compile_module("call_return_code").unwrap(); let (callee_code, _callee_hash) = compile_module("ok_trap_revert").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { @@ -1481,28 +1478,60 @@ mod run_tests { let bob = builder::bare_instantiate(Code::Upload(caller_code)) .value(min_balance * 100) - .data(vec![0]) .build_and_unwrap_contract(); - ::Currency::set_balance(&bob.account_id, min_balance); // Contract calls into Django which is no valid contract + // This will be a balance transfer into a new account + // with more than the contract has which will make the transfer fail + let result = builder::bare_call(bob.addr) + .data( + AsRef::<[u8]>::as_ref(&DJANGO_ADDR) + .iter() + .chain(&u256_bytes(min_balance * 200)) + .cloned() + .collect(), + ) + .build_and_unwrap_result(); + assert_return_code!(result, RuntimeReturnCode::TransferFailed); + + // Sending less than the minimum balance will also make the transfer fail let result = builder::bare_call(bob.addr) - .data(AsRef::<[u8]>::as_ref(&DJANGO).to_vec()) + .data( + AsRef::<[u8]>::as_ref(&DJANGO_ADDR) + .iter() + .chain(&u256_bytes(42)) + .cloned() + .collect(), + ) .build_and_unwrap_result(); - assert_return_code!(result, RuntimeReturnCode::NotCallable); + assert_return_code!(result, RuntimeReturnCode::TransferFailed); + + // Sending at least the minimum balance should result in success but + // no code called. + assert_eq!(test_utils::get_balance(Ð_DJANGO), 0); + let result = builder::bare_call(bob.addr) + .data( + AsRef::<[u8]>::as_ref(&DJANGO_ADDR) + .iter() + .chain(&u256_bytes(55)) + .cloned() + .collect(), + ) + .build_and_unwrap_result(); + assert_return_code!(result, RuntimeReturnCode::Success); + assert_eq!(test_utils::get_balance(Ð_DJANGO), 55); let django = builder::bare_instantiate(Code::Upload(callee_code)) .origin(RuntimeOrigin::signed(CHARLIE)) .value(min_balance * 100) - .data(vec![0]) .build_and_unwrap_contract(); - ::Currency::set_balance(&django.account_id, min_balance); - // Contract has only the minimal balance so any transfer will fail. + // Sending more than the contract has will make the transfer fail. let result = builder::bare_call(bob.addr) .data( AsRef::<[u8]>::as_ref(&django.addr) .iter() + .chain(&u256_bytes(min_balance * 300)) .chain(&0u32.to_le_bytes()) .cloned() .collect(), @@ -1516,6 +1545,7 @@ mod run_tests { .data( AsRef::<[u8]>::as_ref(&django.addr) .iter() + .chain(&u256_bytes(5)) .chain(&1u32.to_le_bytes()) .cloned() .collect(), @@ -1528,6 +1558,7 @@ mod run_tests { .data( AsRef::<[u8]>::as_ref(&django.addr) .iter() + .chain(&u256_bytes(5)) .chain(&2u32.to_le_bytes()) .cloned() .collect(), diff --git a/substrate/frame/revive/src/wasm/runtime.rs b/substrate/frame/revive/src/wasm/runtime.rs index 528b0ababfa0..d9257d38b66e 100644 --- a/substrate/frame/revive/src/wasm/runtime.rs +++ b/substrate/frame/revive/src/wasm/runtime.rs @@ -581,8 +581,7 @@ impl<'a, E: Ext, M: PolkaVmInstance> Runtime<'a, E, M> { None => Some(Err(Error::::InvalidCallFlags.into())), Some(flags) => Some(Ok(ExecReturnValue { flags, data })), }, - Err(TrapReason::Termination) => - Some(Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() })), + Err(TrapReason::Termination) => Some(Ok(Default::default())), Err(TrapReason::SupervisorError(error)) => Some(Err(error.into())), } }, diff --git a/substrate/frame/revive/uapi/src/flags.rs b/substrate/frame/revive/uapi/src/flags.rs index 17b91ce2b3be..763a89d6c030 100644 --- a/substrate/frame/revive/uapi/src/flags.rs +++ b/substrate/frame/revive/uapi/src/flags.rs @@ -20,6 +20,7 @@ use bitflags::bitflags; bitflags! { /// Flags used by a contract to customize exit behaviour. #[cfg_attr(feature = "scale", derive(codec::Encode, codec::Decode, scale_info::TypeInfo))] + #[derive(Default)] pub struct ReturnFlags: u32 { /// If this bit is set all changes made by the contract execution are rolled back. const REVERT = 0x0000_0001; diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index 79f9d298ada7..8a4482f52ad5 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -1027,7 +1027,8 @@ pub mod pallet { return Err(Error::::InsufficientBond.into()) } - frame_system::Pallet::::inc_consumers(&stash).map_err(|_| Error::::BadState)?; + // Would fail if account has no provider. + frame_system::Pallet::::inc_consumers(&stash)?; let stash_balance = T::Currency::free_balance(&stash); let value = value.min(stash_balance); diff --git a/substrate/frame/support/procedural/examples/proc_main/inject_runtime_type.rs b/substrate/frame/support/procedural/examples/proc_main/inject_runtime_type.rs index cd5660f2bda7..d0725ba2049f 100644 --- a/substrate/frame/support/procedural/examples/proc_main/inject_runtime_type.rs +++ b/substrate/frame/support/procedural/examples/proc_main/inject_runtime_type.rs @@ -43,6 +43,7 @@ fn derive_impl_works_with_no_aggregated_types() { type Block = super::Block; type AccountId = super::AccountId; type PalletInfo = super::PalletInfo; + type ExampleConstant = (); } assert_type_eq_all!(::RuntimeOrigin, ()); diff --git a/substrate/frame/support/procedural/examples/proc_main/runtime.rs b/substrate/frame/support/procedural/examples/proc_main/runtime.rs index a9d9281f50da..109ca4f6dc48 100644 --- a/substrate/frame/support/procedural/examples/proc_main/runtime.rs +++ b/substrate/frame/support/procedural/examples/proc_main/runtime.rs @@ -14,6 +14,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +#![allow(deprecated, clippy::deprecated_semver)] use super::{frame_system, Block}; use crate::derive_impl; @@ -121,6 +122,7 @@ mod runtime { // Ensure that the runtime does not export the calls from the pallet #[runtime::pallet_index(4)] #[runtime::disable_call] + #[deprecated = "example"] pub type PalletWithDisabledCall = pallet_with_disabled_call::Pallet; // Ensure that the runtime does not export the unsigned calls from the pallet diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/metadata.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/metadata.rs index daef1b171617..f3724f4ccb69 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/expand/metadata.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/metadata.rs @@ -46,7 +46,7 @@ pub fn expand_runtime_metadata( let index = &decl.index; let storage = expand_pallet_metadata_storage(&filtered_names, runtime, decl); let calls = expand_pallet_metadata_calls(&filtered_names, runtime, decl); - let event = expand_pallet_metadata_events(&filtered_names, runtime, scrate, decl); + let event = expand_pallet_metadata_events(&filtered_names, runtime, decl); let constants = expand_pallet_metadata_constants(runtime, decl); let errors = expand_pallet_metadata_errors(runtime, decl); let docs = expand_pallet_metadata_docs(runtime, decl); @@ -58,7 +58,7 @@ pub fn expand_runtime_metadata( #attr } }); - + let deprecation_info = expand_pallet_metadata_deprecation(runtime, decl); quote! { #attr #scrate::__private::metadata_ir::PalletMetadataIR { @@ -70,6 +70,7 @@ pub fn expand_runtime_metadata( constants: #constants, error: #errors, docs: #docs, + deprecation_info: #deprecation_info, } } }) @@ -200,7 +201,6 @@ fn expand_pallet_metadata_calls( fn expand_pallet_metadata_events( filtered_names: &[&'static str], runtime: &Ident, - scrate: &TokenStream, decl: &Pallet, ) -> TokenStream { if filtered_names.contains(&"Event") { @@ -220,9 +220,7 @@ fn expand_pallet_metadata_events( quote! { Some( - #scrate::__private::metadata_ir::PalletEventMetadataIR { - ty: #scrate::__private::scale_info::meta_type::<#pallet_event>() - } + #pallet_event::event_metadata::<#pallet_event>() ) } } else { @@ -230,6 +228,13 @@ fn expand_pallet_metadata_events( } } +fn expand_pallet_metadata_deprecation(runtime: &Ident, decl: &Pallet) -> TokenStream { + let path = &decl.path; + let instance = decl.instance.as_ref().into_iter(); + + quote! { #path::Pallet::<#runtime #(, #path::#instance)*>::deprecation_info() } +} + fn expand_pallet_metadata_constants(runtime: &Ident, decl: &Pallet) -> TokenStream { let path = &decl.path; let instance = decl.instance.as_ref().into_iter(); diff --git a/substrate/frame/support/procedural/src/construct_runtime/parse.rs b/substrate/frame/support/procedural/src/construct_runtime/parse.rs index 532e032d0cb7..3e38adcc3c59 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/parse.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/parse.rs @@ -130,9 +130,9 @@ impl Parse for WhereSection { definitions.push(definition); if !input.peek(Token![,]) { if !input.peek(token::Brace) { - return Err(input.error("Expected `,` or `{`")) + return Err(input.error("Expected `,` or `{`")); } - break + break; } input.parse::()?; } @@ -144,7 +144,7 @@ impl Parse for WhereSection { "`{:?}` was declared above. Please use exactly one declaration for `{:?}`.", kind, kind ); - return Err(Error::new(*kind_span, msg)) + return Err(Error::new(*kind_span, msg)); } Ok(Self { span: input.span() }) } @@ -173,7 +173,7 @@ impl Parse for WhereDefinition { } else if lookahead.peek(keyword::UncheckedExtrinsic) { (input.parse::()?.span(), WhereKind::UncheckedExtrinsic) } else { - return Err(lookahead.error()) + return Err(lookahead.error()); }; let _: Token![=] = input.parse()?; @@ -270,7 +270,7 @@ impl Parse for PalletDeclaration { { return Err(input.error( "Unexpected tokens, expected one of `::{`, `exclude_parts`, `use_parts`, `=`, `,`", - )) + )); } else { is_expanded.then_some(extra_parts) }; @@ -283,7 +283,7 @@ impl Parse for PalletDeclaration { let _: keyword::use_parts = input.parse()?; SpecifiedParts::Use(parse_pallet_parts_no_generic(input)?) } else if !input.peek(Token![=]) && !input.peek(Token![,]) && !input.is_empty() { - return Err(input.error("Unexpected tokens, expected one of `exclude_parts`, `=`, `,`")) + return Err(input.error("Unexpected tokens, expected one of `exclude_parts`, `=`, `,`")); } else { SpecifiedParts::All }; @@ -295,7 +295,7 @@ impl Parse for PalletDeclaration { let index = index.base10_parse::()?; Some(index) } else if !input.peek(Token![,]) && !input.is_empty() { - return Err(input.error("Unexpected tokens, expected one of `=`, `,`")) + return Err(input.error("Unexpected tokens, expected one of `=`, `,`")); } else { None }; @@ -339,7 +339,7 @@ impl Parse for PalletPath { let ident = input.call(Ident::parse_any)?; res.inner.segments.push(ident.into()); } else { - return Err(lookahead.error()) + return Err(lookahead.error()); } while input.peek(Token![::]) && input.peek3(Ident) { @@ -370,7 +370,7 @@ fn parse_pallet_parts(input: ParseStream) -> Result> { "`{}` was already declared before. Please remove the duplicate declaration", part.name(), ); - return Err(Error::new(part.keyword.span(), msg)) + return Err(Error::new(part.keyword.span(), msg)); } } @@ -505,7 +505,7 @@ impl Parse for PalletPart { keyword.name(), valid_generics, ); - return Err(syn::Error::new(keyword.span(), msg)) + return Err(syn::Error::new(keyword.span(), msg)); } Ok(Self { keyword, generics }) @@ -566,7 +566,7 @@ fn parse_pallet_parts_no_generic(input: ParseStream) -> Result, /// The doc literals pub docs: Vec, + /// attributes + pub attrs: Vec, } impl Pallet { @@ -650,7 +652,7 @@ enum PalletsConversion { /// incrementally from last explicit or 0. fn convert_pallets(pallets: Vec) -> syn::Result { if pallets.iter().any(|pallet| pallet.pallet_parts.is_none()) { - return Ok(PalletsConversion::Implicit(pallets)) + return Ok(PalletsConversion::Implicit(pallets)); } let mut indices = HashMap::new(); @@ -678,7 +680,7 @@ fn convert_pallets(pallets: Vec) -> syn::Result) -> syn::Result) -> syn::Result (), @@ -738,7 +740,7 @@ fn convert_pallets(pallets: Vec) -> syn::Result) -> syn::Result>>()?; diff --git a/substrate/frame/support/procedural/src/deprecation.rs b/substrate/frame/support/procedural/src/deprecation.rs new file mode 100644 index 000000000000..bdf996d21229 --- /dev/null +++ b/substrate/frame/support/procedural/src/deprecation.rs @@ -0,0 +1,181 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use proc_macro2::TokenStream; +use quote::quote; +use syn::{ + punctuated::Punctuated, spanned::Spanned, Error, Expr, ExprLit, Lit, Meta, MetaNameValue, + Result, Token, Variant, +}; + +fn deprecation_msg_formatter(msg: &str) -> String { + format!( + r#"{msg} + help: the following are the possible correct uses +| +| #[deprecated = "reason"] +| +| #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] +| +| #[deprecated] +|"# + ) +} + +fn parse_deprecated_meta(crate_: &TokenStream, attr: &syn::Attribute) -> Result { + match &attr.meta { + Meta::List(meta_list) => { + let parsed = meta_list + .parse_args_with(Punctuated::::parse_terminated) + .map_err(|e| Error::new(attr.span(), e.to_string()))?; + let (note, since) = parsed.iter().try_fold((None, None), |mut acc, item| { + let value = match &item.value { + Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }) => Ok(lit), + _ => Err(Error::new( + attr.span(), + deprecation_msg_formatter( + "Invalid deprecation attribute: expected string literal", + ), + )), + }?; + if item.path.is_ident("note") { + acc.0.replace(value); + } else if item.path.is_ident("since") { + acc.1.replace(value); + } + Ok::<(Option<&syn::Lit>, Option<&syn::Lit>), Error>(acc) + })?; + note.map_or_else( + || Err(Error::new(attr.span(), deprecation_msg_formatter("Invalid deprecation attribute: missing `note`"))), + |note| { + let since = if let Some(str) = since { + quote! { Some(#str) } + } else { + quote! { None } + }; + let doc = quote! { #crate_::__private::metadata_ir::DeprecationStatusIR::Deprecated { note: #note, since: #since }}; + Ok(doc) + }, + ) + }, + Meta::NameValue(MetaNameValue { + value: Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }), + .. + }) => { + // #[deprecated = "lit"] + let doc = quote! { #crate_::__private::metadata_ir::DeprecationStatusIR::Deprecated { note: #lit, since: None } }; + Ok(doc) + }, + Meta::Path(_) => { + // #[deprecated] + Ok( + quote! { #crate_::__private::metadata_ir::DeprecationStatusIR::DeprecatedWithoutNote }, + ) + }, + _ => Err(Error::new( + attr.span(), + deprecation_msg_formatter("Invalid deprecation attribute: expected string literal"), + )), + } +} + +/// collects deprecation attribute if its present. +pub fn get_deprecation(path: &TokenStream, attrs: &[syn::Attribute]) -> Result { + parse_deprecation(path, attrs).map(|item| { + item.unwrap_or_else(|| { + quote! {#path::__private::metadata_ir::DeprecationStatusIR::NotDeprecated} + }) + }) +} + +fn parse_deprecation(path: &TokenStream, attrs: &[syn::Attribute]) -> Result> { + attrs + .iter() + .find(|a| a.path().is_ident("deprecated")) + .map(|a| parse_deprecated_meta(path, a)) + .transpose() +} + +/// collects deprecation attribute if its present for enum-like types +pub fn get_deprecation_enum<'a>( + path: &TokenStream, + parent_attrs: &[syn::Attribute], + children_attrs: impl Iterator, +) -> Result { + let parent_deprecation = parse_deprecation(path, parent_attrs)?; + + let children = children_attrs + .filter_map(|(key, attributes)| { + let key = quote::quote! { #path::__private::codec::Compact(#key as u8) }; + let deprecation_status = parse_deprecation(path, attributes).transpose(); + deprecation_status.map(|item| item.map(|item| quote::quote! { (#key, #item) })) + }) + .collect::>>()?; + match (parent_deprecation, children.as_slice()) { + (None, []) => + Ok(quote::quote! { #path::__private::metadata_ir::DeprecationInfoIR::NotDeprecated }), + (None, _) => { + let children = quote::quote! { #path::__private::scale_info::prelude::collections::BTreeMap::from([#( #children),*]) }; + Ok( + quote::quote! { #path::__private::metadata_ir::DeprecationInfoIR::VariantsDeprecated(#children) }, + ) + }, + (Some(depr), _) => Ok( + quote::quote! { #path::__private::metadata_ir::DeprecationInfoIR::ItemDeprecated(#depr) }, + ), + } +} + +/// Gets the index for the variant inside `Error` or `Event` declaration. +/// priority is as follows: +/// Manual `#[codec(index = N)]` +/// Explicit discriminant `Variant = N` +/// Variant's definition index +pub fn variant_index_for_deprecation(index: u8, item: &Variant) -> u8 { + let index: u8 = + if let Some((_, Expr::Lit(ExprLit { lit: Lit::Int(num_lit), .. }))) = &item.discriminant { + num_lit.base10_parse::().unwrap_or(index as u8) + } else { + index as u8 + }; + + item.attrs + .iter() + .find(|attr| attr.path().is_ident("codec")) + .and_then(|attr| { + if let Meta::List(meta_list) = &attr.meta { + meta_list + .parse_args_with(Punctuated::::parse_terminated) + .ok() + } else { + None + } + }) + .and_then(|parsed| { + parsed.iter().fold(None, |mut acc, item| { + if let Expr::Lit(ExprLit { lit: Lit::Int(num_lit), .. }) = &item.value { + num_lit.base10_parse::().iter().for_each(|val| { + if item.path.is_ident("index") { + acc.replace(*val); + } + }) + }; + acc + }) + }) + .unwrap_or(index) +} diff --git a/substrate/frame/support/procedural/src/lib.rs b/substrate/frame/support/procedural/src/lib.rs index ef99faee86ae..8554a5b830de 100644 --- a/substrate/frame/support/procedural/src/lib.rs +++ b/substrate/frame/support/procedural/src/lib.rs @@ -23,6 +23,7 @@ mod benchmark; mod construct_runtime; mod crate_version; +mod deprecation; mod derive_impl; mod dummy_part_checker; mod dynamic_params; @@ -812,7 +813,7 @@ pub fn inject_runtime_type(_: TokenStream, tokens: TokenStream) -> TokenStream { `RuntimeTask`, `RuntimeOrigin`, `RuntimeParameters` or `PalletInfo`", ) .to_compile_error() - .into() + .into(); } tokens } @@ -1221,7 +1222,7 @@ pub fn import_section(attr: TokenStream, tokens: TokenStream) -> TokenStream { "`#[import_section]` can only be applied to a valid pallet module", ) .to_compile_error() - .into() + .into(); } if let Some(ref mut content) = internal_mod.content { diff --git a/substrate/frame/support/procedural/src/pallet/expand/call.rs b/substrate/frame/support/procedural/src/pallet/expand/call.rs index 5dc8dc3146cf..8b333d19087d 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/call.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/call.rs @@ -263,6 +263,15 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { } }); + let deprecation = match crate::deprecation::get_deprecation_enum( + "e::quote! {#frame_support}, + def.call.as_ref().map(|call| call.attrs.as_ref()).unwrap_or(&[]), + methods.iter().map(|item| (item.call_index as u8, item.attrs.as_ref())), + ) { + Ok(deprecation) => deprecation, + Err(e) => return e.into_compile_error(), + }; + quote::quote_spanned!(span => #[doc(hidden)] mod warnings { @@ -463,7 +472,10 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { #[allow(dead_code)] #[doc(hidden)] pub fn call_functions() -> #frame_support::__private::metadata_ir::PalletCallMetadataIR { - #frame_support::__private::scale_info::meta_type::<#call_ident<#type_use_gen>>().into() + #frame_support::__private::metadata_ir::PalletCallMetadataIR { + ty: #frame_support::__private::scale_info::meta_type::<#call_ident<#type_use_gen>>(), + deprecation_info: #deprecation, + } } } ) diff --git a/substrate/frame/support/procedural/src/pallet/expand/constants.rs b/substrate/frame/support/procedural/src/pallet/expand/constants.rs index a36df790bd29..ca34631e7494 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/constants.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/constants.rs @@ -28,6 +28,8 @@ struct ConstDef { pub default_byte_impl: proc_macro2::TokenStream, /// Constant name for Metadata (optional) pub metadata_name: Option, + /// Deprecation_info: + pub deprecation_info: proc_macro2::TokenStream, } /// Implement the `pallet_constants_metadata` function for the pallet. @@ -42,11 +44,18 @@ pub fn expand_constants(def: &mut Def) -> proc_macro2::TokenStream { where_clauses.extend(def.extra_constants.iter().map(|d| &d.where_clause)); let completed_where_clause = super::merge_where_clauses(&where_clauses); - let config_consts = def.config.consts_metadata.iter().map(|const_| { + let mut config_consts = vec![]; + for const_ in def.config.consts_metadata.iter() { let ident = &const_.ident; let const_type = &const_.type_; - - ConstDef { + let deprecation_info = match crate::deprecation::get_deprecation( + "e::quote! { #frame_support }, + &const_.attrs, + ) { + Ok(deprecation) => deprecation, + Err(e) => return e.into_compile_error(), + }; + config_consts.push(ConstDef { ident: const_.ident.clone(), type_: const_.type_.clone(), doc: const_.doc.clone(), @@ -56,13 +65,22 @@ pub fn expand_constants(def: &mut Def) -> proc_macro2::TokenStream { #frame_support::__private::codec::Encode::encode(&value) ), metadata_name: None, - } - }); + deprecation_info, + }) + } - let extra_consts = def.extra_constants.iter().flat_map(|d| &d.extra_constants).map(|const_| { + let mut extra_consts = vec![]; + for const_ in def.extra_constants.iter().flat_map(|d| &d.extra_constants) { let ident = &const_.ident; - - ConstDef { + let deprecation_info = match crate::deprecation::get_deprecation( + "e::quote! { #frame_support }, + &const_.attrs, + ) { + Ok(deprecation) => deprecation, + Err(e) => return e.into_compile_error(), + }; + + extra_consts.push(ConstDef { ident: const_.ident.clone(), type_: const_.type_.clone(), doc: const_.doc.clone(), @@ -71,10 +89,11 @@ pub fn expand_constants(def: &mut Def) -> proc_macro2::TokenStream { #frame_support::__private::codec::Encode::encode(&value) ), metadata_name: const_.metadata_name.clone(), - } - }); + deprecation_info, + }) + } - let consts = config_consts.chain(extra_consts).map(|const_| { + let consts = config_consts.into_iter().chain(extra_consts.into_iter()).map(|const_| { let const_type = &const_.type_; let ident_str = format!("{}", const_.metadata_name.unwrap_or(const_.ident)); @@ -82,13 +101,14 @@ pub fn expand_constants(def: &mut Def) -> proc_macro2::TokenStream { let doc = if cfg!(feature = "no-metadata-docs") { &no_docs } else { &const_.doc }; let default_byte_impl = &const_.default_byte_impl; - + let deprecation_info = &const_.deprecation_info; quote::quote!({ #frame_support::__private::metadata_ir::PalletConstantMetadataIR { name: #ident_str, ty: #frame_support::__private::scale_info::meta_type::<#const_type>(), value: { #default_byte_impl }, docs: #frame_support::__private::vec![ #( #doc ),* ], + deprecation_info: #deprecation_info } }) }); diff --git a/substrate/frame/support/procedural/src/pallet/expand/error.rs b/substrate/frame/support/procedural/src/pallet/expand/error.rs index 05478ee39084..646655cfe14e 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/error.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/error.rs @@ -104,6 +104,19 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream { let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" }; + let deprecation = match crate::deprecation::get_deprecation_enum( + "e::quote! {#frame_support}, + &error.attrs, + error_item.variants.iter().enumerate().map(|(index, item)| { + let index = crate::deprecation::variant_index_for_deprecation(index as u8, item); + + (index, item.attrs.as_ref()) + }), + ) { + Ok(deprecation) => deprecation, + Err(e) => return e.into_compile_error(), + }; + // derive TypeInfo for error metadata error_item.attrs.push(syn::parse_quote! { #[derive( @@ -189,5 +202,16 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream { } pub use #error_token_unique_id as tt_error_token; + + impl<#type_impl_gen> #error_ident<#type_use_gen> #config_where_clause { + #[allow(dead_code)] + #[doc(hidden)] + pub fn error_metadata() -> #frame_support::__private::metadata_ir::PalletErrorMetadataIR { + #frame_support::__private::metadata_ir::PalletErrorMetadataIR { + ty: #frame_support::__private::scale_info::meta_type::<#error_ident<#type_use_gen>>(), + deprecation_info: #deprecation, + } + } + } ) } diff --git a/substrate/frame/support/procedural/src/pallet/expand/event.rs b/substrate/frame/support/procedural/src/pallet/expand/event.rs index 655fc5507d26..8519143179d6 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/event.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/event.rs @@ -71,7 +71,6 @@ pub fn expand_event(def: &mut Def) -> proc_macro2::TokenStream { let frame_support = &def.frame_support; let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span); let event_impl_gen = &event.gen_kind.type_impl_gen(event.attr_span); - let event_item = { let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[event.index]; if let syn::Item::Enum(item) = item { @@ -96,6 +95,19 @@ pub fn expand_event(def: &mut Def) -> proc_macro2::TokenStream { event_item.variants.push(variant); } + let deprecation = match crate::deprecation::get_deprecation_enum( + "e::quote! {#frame_support}, + &event.attrs, + event_item.variants.iter().enumerate().map(|(index, item)| { + let index = crate::deprecation::variant_index_for_deprecation(index as u8, item); + + (index, item.attrs.as_ref()) + }), + ) { + Ok(deprecation) => deprecation, + Err(e) => return e.into_compile_error(), + }; + if get_doc_literals(&event_item.attrs).is_empty() { event_item .attrs @@ -170,5 +182,16 @@ pub fn expand_event(def: &mut Def) -> proc_macro2::TokenStream { impl<#event_impl_gen> From<#event_ident<#event_use_gen>> for () #event_where_clause { fn from(_: #event_ident<#event_use_gen>) {} } + + impl<#event_impl_gen> #event_ident<#event_use_gen> #event_where_clause { + #[allow(dead_code)] + #[doc(hidden)] + pub fn event_metadata() -> #frame_support::__private::metadata_ir::PalletEventMetadataIR { + #frame_support::__private::metadata_ir::PalletEventMetadataIR { + ty: #frame_support::__private::scale_info::meta_type::(), + deprecation_info: #deprecation, + } + } + } ) } diff --git a/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs b/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs index 7ebc4bb2e9dc..c6166ff45b19 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs @@ -83,9 +83,7 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { impl<#type_impl_gen> #pallet_ident<#type_use_gen> #config_where_clause { #[doc(hidden)] pub fn error_metadata() -> Option<#frame_support::__private::metadata_ir::PalletErrorMetadataIR> { - Some(#frame_support::__private::metadata_ir::PalletErrorMetadataIR { - ty: #frame_support::__private::scale_info::meta_type::<#error_ident<#type_use_gen>>() - }) + Some(<#error_ident<#type_use_gen>>::error_metadata()) } } ) @@ -187,7 +185,12 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { } } ]; - + let deprecation_status = + match crate::deprecation::get_deprecation("e::quote! {#frame_support}, &def.item.attrs) + { + Ok(deprecation) => deprecation, + Err(e) => return e.into_compile_error(), + }; quote::quote_spanned!(def.pallet_struct.attr_span => #pallet_error_metadata @@ -286,5 +289,13 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { #storage_info #whitelisted_storage_keys_impl + + impl<#type_use_gen> #pallet_ident<#type_use_gen> { + #[allow(dead_code)] + #[doc(hidden)] + pub fn deprecation_info() -> #frame_support::__private::metadata_ir::DeprecationStatusIR { + #deprecation_status + } + } ) } diff --git a/substrate/frame/support/procedural/src/pallet/expand/storage.rs b/substrate/frame/support/procedural/src/pallet/expand/storage.rs index 9abb3029bbd9..e5bfa2793cbb 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/storage.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/storage.rs @@ -62,7 +62,7 @@ fn check_prefix_duplicates( if let Some(other_dup_err) = used_prefixes.insert(prefix.clone(), dup_err.clone()) { let mut err = dup_err; err.combine(other_dup_err); - return Err(err) + return Err(err); } if let Metadata::CountedMap { .. } = storage_def.metadata { @@ -79,7 +79,7 @@ fn check_prefix_duplicates( if let Some(other_dup_err) = used_prefixes.insert(counter_prefix, counter_dup_err.clone()) { let mut err = counter_dup_err; err.combine(other_dup_err); - return Err(err) + return Err(err); } } @@ -152,7 +152,7 @@ pub fn process_generics(def: &mut Def) -> syn::Result syn::Result proc_macro2::TokenStream { .filter_map(|storage_def| check_prefix_duplicates(storage_def, &mut prefix_set).err()); if let Some(mut final_error) = errors.next() { errors.for_each(|error| final_error.combine(error)); - return final_error.into_compile_error() + return final_error.into_compile_error(); } let frame_support = &def.frame_support; let frame_system = &def.frame_system; let pallet_ident = &def.pallet_struct.pallet; - - let entries_builder = def.storages.iter().map(|storage| { + let mut entries_builder = vec![]; + for storage in def.storages.iter() { let no_docs = vec![]; let docs = if cfg!(feature = "no-metadata-docs") { &no_docs } else { &storage.docs }; @@ -418,19 +418,26 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { let full_ident = quote::quote_spanned!(storage.attr_span => #ident<#gen> ); let cfg_attrs = &storage.cfg_attrs; - - quote::quote_spanned!(storage.attr_span => + let deprecation = match crate::deprecation::get_deprecation( + "e::quote! { #frame_support }, + &storage.attrs, + ) { + Ok(deprecation) => deprecation, + Err(e) => return e.into_compile_error(), + }; + entries_builder.push(quote::quote_spanned!(storage.attr_span => #(#cfg_attrs)* { <#full_ident as #frame_support::storage::StorageEntryMetadataBuilder>::build_metadata( + #deprecation, #frame_support::__private::vec![ #( #docs, )* ], &mut entries, ); } - ) - }); + )) + } let getters = def.storages.iter().map(|storage| { if let Some(getter) = &storage.getter { diff --git a/substrate/frame/support/procedural/src/pallet/parse/call.rs b/substrate/frame/support/procedural/src/pallet/parse/call.rs index 68c2cb8bd1b3..346dff46f12e 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/call.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/call.rs @@ -51,6 +51,8 @@ pub struct CallDef { pub docs: Vec, /// The optional `weight` attribute on the `pallet::call`. pub inherited_call_weight: Option, + /// attributes + pub attrs: Vec, } /// The weight of a call. @@ -124,7 +126,7 @@ impl syn::parse::Parse for FunctionAttr { let index = call_index_content.parse::()?; if !index.suffix().is_empty() { let msg = "Number literal must not have a suffix"; - return Err(syn::Error::new(index.span(), msg)) + return Err(syn::Error::new(index.span(), msg)); } Ok(FunctionAttr::CallIndex(index.base10_parse()?)) } else if lookahead.peek(keyword::feeless_if) { @@ -200,9 +202,9 @@ pub fn check_dispatchable_first_arg_type(ty: &syn::Type, is_ref: bool) -> syn::R } else { "Invalid type: expected `OriginFor` or `T::RuntimeOrigin`" }; - return Err(syn::Error::new(ty.span(), msg)) + return Err(syn::Error::new(ty.span(), msg)); }, - } + }; } impl CallDef { @@ -216,9 +218,8 @@ impl CallDef { let item_impl = if let syn::Item::Impl(item) = item { item } else { - return Err(syn::Error::new(item.span(), "Invalid pallet::call, expected item impl")) + return Err(syn::Error::new(item.span(), "Invalid pallet::call, expected item impl")); }; - let instances = vec![ helper::check_impl_gen(&item_impl.generics, item_impl.impl_token.span())?, helper::check_pallet_struct_usage(&item_impl.self_ty)?, @@ -227,7 +228,7 @@ impl CallDef { if let Some((_, _, for_)) = item_impl.trait_ { let msg = "Invalid pallet::call, expected no trait ident as in \ `impl<..> Pallet<..> { .. }`"; - return Err(syn::Error::new(for_.span(), msg)) + return Err(syn::Error::new(for_.span(), msg)); } let mut methods = vec![]; @@ -244,18 +245,18 @@ impl CallDef { _ => method.vis.span(), }; - return Err(syn::Error::new(span, msg)) + return Err(syn::Error::new(span, msg)); } match method.sig.inputs.first() { None => { let msg = "Invalid pallet::call, must have at least origin arg"; - return Err(syn::Error::new(method.sig.span(), msg)) + return Err(syn::Error::new(method.sig.span(), msg)); }, Some(syn::FnArg::Receiver(_)) => { let msg = "Invalid pallet::call, first argument must be a typed argument, \ e.g. `origin: OriginFor`"; - return Err(syn::Error::new(method.sig.span(), msg)) + return Err(syn::Error::new(method.sig.span(), msg)); }, Some(syn::FnArg::Typed(arg)) => { check_dispatchable_first_arg_type(&arg.ty, false)?; @@ -304,13 +305,13 @@ impl CallDef { }, _ => { let msg = "Invalid pallet::call, too many weight attributes given"; - return Err(syn::Error::new(method.sig.span(), msg)) + return Err(syn::Error::new(method.sig.span(), msg)); }, }; if call_idx_attrs.len() > 1 { let msg = "Invalid pallet::call, too many call_index attributes given"; - return Err(syn::Error::new(method.sig.span(), msg)) + return Err(syn::Error::new(method.sig.span(), msg)); } let call_index = call_idx_attrs.pop().map(|attr| match attr { FunctionAttr::CallIndex(idx) => idx, @@ -335,7 +336,7 @@ impl CallDef { ); let mut err = syn::Error::new(used_fn.span(), &msg); err.combine(syn::Error::new(method.sig.ident.span(), msg)); - return Err(err) + return Err(err); } let mut args = vec![]; @@ -351,14 +352,14 @@ impl CallDef { if arg_attrs.len() > 1 { let msg = "Invalid pallet::call, argument has too many attributes"; - return Err(syn::Error::new(arg.span(), msg)) + return Err(syn::Error::new(arg.span(), msg)); } let arg_ident = if let syn::Pat::Ident(pat) = &*arg.pat { pat.ident.clone() } else { let msg = "Invalid pallet::call, argument must be ident"; - return Err(syn::Error::new(arg.pat.span(), msg)) + return Err(syn::Error::new(arg.pat.span(), msg)); }; args.push((!arg_attrs.is_empty(), arg_ident, arg.ty.clone())); @@ -368,7 +369,7 @@ impl CallDef { if feeless_attrs.len() > 1 { let msg = "Invalid pallet::call, there can only be one feeless_if attribute"; - return Err(syn::Error::new(feeless_attrs[1].0, msg)) + return Err(syn::Error::new(feeless_attrs[1].0, msg)); } let feeless_check: Option = feeless_attrs.pop().map(|(_, attr)| match attr { @@ -380,13 +381,13 @@ impl CallDef { if feeless_check.inputs.len() != args.len() + 1 { let msg = "Invalid pallet::call, feeless_if closure must have same \ number of arguments as the dispatchable function"; - return Err(syn::Error::new(feeless_check.span(), msg)) + return Err(syn::Error::new(feeless_check.span(), msg)); } match feeless_check.inputs.first() { None => { let msg = "Invalid pallet::call, feeless_if closure must have at least origin arg"; - return Err(syn::Error::new(feeless_check.span(), msg)) + return Err(syn::Error::new(feeless_check.span(), msg)); }, Some(syn::Pat::Type(arg)) => { check_dispatchable_first_arg_type(&arg.ty, true)?; @@ -394,7 +395,7 @@ impl CallDef { _ => { let msg = "Invalid pallet::call, feeless_if closure first argument must be a typed argument, \ e.g. `origin: OriginFor`"; - return Err(syn::Error::new(feeless_check.span(), msg)) + return Err(syn::Error::new(feeless_check.span(), msg)); }, } @@ -405,18 +406,18 @@ impl CallDef { pat.elem.clone() } else { let msg = "Invalid pallet::call, feeless_if closure argument must be a reference"; - return Err(syn::Error::new(ty.span(), msg)) + return Err(syn::Error::new(ty.span(), msg)); } } else { let msg = "Invalid pallet::call, feeless_if closure argument must be a type ascription pattern"; - return Err(syn::Error::new(feeless_arg.span(), msg)) + return Err(syn::Error::new(feeless_arg.span(), msg)); }; if feeless_arg_type != arg.2 { let msg = "Invalid pallet::call, feeless_if closure argument must have \ a reference to the same type as the dispatchable function argument"; - return Err(syn::Error::new(feeless_arg.span(), msg)) + return Err(syn::Error::new(feeless_arg.span(), msg)); } } @@ -429,7 +430,7 @@ impl CallDef { }; if !valid_return { let msg = "Invalid pallet::call, feeless_if closure must return `bool`"; - return Err(syn::Error::new(feeless_check.output.span(), msg)) + return Err(syn::Error::new(feeless_check.output.span(), msg)); } } @@ -447,7 +448,7 @@ impl CallDef { }); } else { let msg = "Invalid pallet::call, only method accepted"; - return Err(syn::Error::new(item.span(), msg)) + return Err(syn::Error::new(item.span(), msg)); } } @@ -459,6 +460,7 @@ impl CallDef { where_clause: item_impl.generics.where_clause.clone(), docs: get_doc_literals(&item_impl.attrs), inherited_call_weight, + attrs: item_impl.attrs.clone(), }) } } diff --git a/substrate/frame/support/procedural/src/pallet/parse/config.rs b/substrate/frame/support/procedural/src/pallet/parse/config.rs index 6febaac9ffa3..9a59d7114202 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/config.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/config.rs @@ -78,6 +78,8 @@ pub struct ConstMetadataDef { pub type_: syn::Type, /// The doc associated pub doc: Vec, + /// attributes + pub attrs: Vec, } impl TryFrom<&syn::TraitItemType> for ConstMetadataDef { @@ -100,22 +102,22 @@ impl TryFrom<&syn::TraitItemType> for ConstMetadataDef { .ok_or_else(|| err(trait_ty.span(), "`Get` trait bound not found"))?; let syn::PathArguments::AngleBracketed(ref ab) = bound.arguments else { - return Err(err(bound.span(), "Expected trait generic args")) + return Err(err(bound.span(), "Expected trait generic args")); }; // Only one type argument is expected. if ab.args.len() != 1 { - return Err(err(bound.span(), "Expected a single type argument")) + return Err(err(bound.span(), "Expected a single type argument")); } let syn::GenericArgument::Type(ref type_arg) = ab.args[0] else { - return Err(err(ab.args[0].span(), "Expected a type argument")) + return Err(err(ab.args[0].span(), "Expected a type argument")); }; let type_ = syn::parse2::(replace_self_by_t(type_arg.to_token_stream())) .expect("Internal error: replacing `Self` by `T` should result in valid type"); - Ok(Self { ident, type_, doc }) + Ok(Self { ident, type_, doc, attrs: trait_ty.attrs.clone() }) } } @@ -220,7 +222,7 @@ fn check_event_type( let syn::TraitItem::Type(type_) = trait_item else { return Ok(false) }; if type_.ident != "RuntimeEvent" { - return Ok(false) + return Ok(false); } // Check event has no generics @@ -228,7 +230,7 @@ fn check_event_type( let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must have\ no generics nor where_clause"; - return Err(syn::Error::new(trait_item.span(), msg)) + return Err(syn::Error::new(trait_item.span(), msg)); } // Check bound contains IsType and From @@ -242,7 +244,7 @@ fn check_event_type( "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \ bound: `IsType<::RuntimeEvent>`" .to_string(); - return Err(syn::Error::new(type_.span(), msg)) + return Err(syn::Error::new(type_.span(), msg)); } let from_event_bound = type_ @@ -254,7 +256,7 @@ fn check_event_type( let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \ bound: `From` or `From>` or `From>`"; - return Err(syn::Error::new(type_.span(), msg)) + return Err(syn::Error::new(type_.span(), msg)); }; if from_event_bound.is_generic && (from_event_bound.has_instance != trait_has_instance) { @@ -262,7 +264,7 @@ fn check_event_type( "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` bounds inconsistent \ `From`. Config and generic Event must be both with instance or \ without instance"; - return Err(syn::Error::new(type_.span(), msg)) + return Err(syn::Error::new(type_.span(), msg)); } Ok(true) @@ -274,7 +276,7 @@ fn check_event_type( fn has_expected_system_config(path: syn::Path, frame_system: &syn::Path) -> bool { // Check if `frame_system` is actually 'frame_system'. if path.segments.iter().all(|s| s.ident != "frame_system") { - return false + return false; } let mut expected_system_config = @@ -329,12 +331,12 @@ impl ConfigDef { ) -> syn::Result { let syn::Item::Trait(item) = item else { let msg = "Invalid pallet::config, expected trait definition"; - return Err(syn::Error::new(item.span(), msg)) + return Err(syn::Error::new(item.span(), msg)); }; if !matches!(item.vis, syn::Visibility::Public(_)) { let msg = "Invalid pallet::config, trait must be public"; - return Err(syn::Error::new(item.span(), msg)) + return Err(syn::Error::new(item.span(), msg)); } syn::parse2::(item.ident.to_token_stream())?; @@ -349,7 +351,7 @@ impl ConfigDef { if item.generics.params.len() > 1 { let msg = "Invalid pallet::config, expected no more than one generic"; - return Err(syn::Error::new(item.generics.params[2].span(), msg)) + return Err(syn::Error::new(item.generics.params[2].span(), msg)); } let has_instance = if item.generics.params.first().is_some() { @@ -391,7 +393,7 @@ impl ConfigDef { return Err(syn::Error::new( pallet_attr._bracket.span.join(), "Duplicate #[pallet::constant] attribute not allowed.", - )) + )); } already_constant = true; consts_metadata.push(ConstMetadataDef::try_from(typ)?); @@ -407,13 +409,13 @@ impl ConfigDef { pallet_attr._bracket.span.join(), "`#[pallet:no_default]` can only be used if `#[pallet::config(with_default)]` \ has been specified" - )) + )); } if already_no_default { return Err(syn::Error::new( pallet_attr._bracket.span.join(), "Duplicate #[pallet::no_default] attribute not allowed.", - )) + )); } already_no_default = true; @@ -424,13 +426,13 @@ impl ConfigDef { pallet_attr._bracket.span.join(), "`#[pallet:no_default_bounds]` can only be used if `#[pallet::config(with_default)]` \ has been specified" - )) + )); } if already_no_default_bounds { return Err(syn::Error::new( pallet_attr._bracket.span.join(), "Duplicate #[pallet::no_default_bounds] attribute not allowed.", - )) + )); } already_no_default_bounds = true; }, @@ -472,7 +474,7 @@ impl ConfigDef { frame_system.to_token_stream(), found, ); - return Err(syn::Error::new(item.span(), msg)) + return Err(syn::Error::new(item.span(), msg)); } Ok(Self { diff --git a/substrate/frame/support/procedural/src/pallet/parse/error.rs b/substrate/frame/support/procedural/src/pallet/parse/error.rs index bc4087a0ea76..1a44e79cf0b8 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/error.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/error.rs @@ -53,6 +53,8 @@ pub struct ErrorDef { pub error: keyword::Error, /// The span of the pallet::error attribute. pub attr_span: proc_macro2::Span, + /// Attributes + pub attrs: Vec, } impl ErrorDef { @@ -102,6 +104,6 @@ impl ErrorDef { }) .collect::>()?; - Ok(ErrorDef { attr_span, index, variants, instances, error }) + Ok(ErrorDef { attr_span, index, variants, instances, error, attrs: item.attrs.clone() }) } } diff --git a/substrate/frame/support/procedural/src/pallet/parse/event.rs b/substrate/frame/support/procedural/src/pallet/parse/event.rs index 0fb8ee4f5497..518a9597e2a0 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/event.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/event.rs @@ -43,6 +43,8 @@ pub struct EventDef { pub where_clause: Option, /// The span of the pallet::event attribute. pub attr_span: proc_macro2::Span, + /// event attributes + pub attrs: Vec, } /// Attribute for a pallet's Event. @@ -106,7 +108,7 @@ impl EventDef { } else { return Err(syn::Error::new(item.span(), "Invalid pallet::event, expected enum item")) }; - + let attrs = item.attrs.clone(); let event_attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; let attr_info = PalletEventAttrInfo::from_attrs(event_attrs)?; @@ -136,6 +138,15 @@ impl EventDef { let event = syn::parse2::(item.ident.to_token_stream())?; - Ok(EventDef { attr_span, index, instances, deposit_event, event, gen_kind, where_clause }) + Ok(EventDef { + attr_span, + index, + instances, + deposit_event, + event, + gen_kind, + where_clause, + attrs, + }) } } diff --git a/substrate/frame/support/procedural/src/pallet/parse/extra_constants.rs b/substrate/frame/support/procedural/src/pallet/parse/extra_constants.rs index 12a373db180c..d1960d8c6f01 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/extra_constants.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/extra_constants.rs @@ -51,6 +51,8 @@ pub struct ExtraConstantDef { pub doc: Vec, /// Optional MetaData Name pub metadata_name: Option, + /// Attributes + pub attrs: Vec, } /// Attributes for functions in extra_constants impl block. @@ -82,7 +84,7 @@ impl ExtraConstantsDef { return Err(syn::Error::new( item.span(), "Invalid pallet::extra_constants, expected item impl", - )) + )); }; let instances = vec![ @@ -93,7 +95,7 @@ impl ExtraConstantsDef { if let Some((_, _, for_)) = item.trait_ { let msg = "Invalid pallet::call, expected no trait ident as in \ `impl<..> Pallet<..> { .. }`"; - return Err(syn::Error::new(for_.span(), msg)) + return Err(syn::Error::new(for_.span(), msg)); } let mut extra_constants = vec![]; @@ -102,28 +104,28 @@ impl ExtraConstantsDef { method } else { let msg = "Invalid pallet::call, only method accepted"; - return Err(syn::Error::new(impl_item.span(), msg)) + return Err(syn::Error::new(impl_item.span(), msg)); }; if !method.sig.inputs.is_empty() { let msg = "Invalid pallet::extra_constants, method must have 0 args"; - return Err(syn::Error::new(method.sig.span(), msg)) + return Err(syn::Error::new(method.sig.span(), msg)); } if !method.sig.generics.params.is_empty() { let msg = "Invalid pallet::extra_constants, method must have 0 generics"; - return Err(syn::Error::new(method.sig.generics.params[0].span(), msg)) + return Err(syn::Error::new(method.sig.generics.params[0].span(), msg)); } if method.sig.generics.where_clause.is_some() { let msg = "Invalid pallet::extra_constants, method must have no where clause"; - return Err(syn::Error::new(method.sig.generics.where_clause.span(), msg)) + return Err(syn::Error::new(method.sig.generics.where_clause.span(), msg)); } let type_ = match &method.sig.output { syn::ReturnType::Default => { let msg = "Invalid pallet::extra_constants, method must have a return type"; - return Err(syn::Error::new(method.span(), msg)) + return Err(syn::Error::new(method.span(), msg)); }, syn::ReturnType::Type(_, type_) => *type_.clone(), }; @@ -135,7 +137,7 @@ impl ExtraConstantsDef { if extra_constant_attrs.len() > 1 { let msg = "Invalid attribute in pallet::constant_name, only one attribute is expected"; - return Err(syn::Error::new(extra_constant_attrs[1].metadata_name.span(), msg)) + return Err(syn::Error::new(extra_constant_attrs[1].metadata_name.span(), msg)); } let metadata_name = extra_constant_attrs.pop().map(|attr| attr.metadata_name); @@ -145,6 +147,7 @@ impl ExtraConstantsDef { type_, doc: get_doc_literals(&method.attrs), metadata_name, + attrs: method.attrs.clone(), }); } diff --git a/substrate/frame/support/procedural/src/pallet/parse/mod.rs b/substrate/frame/support/procedural/src/pallet/parse/mod.rs index f55b166c7917..b9c7afcab0f9 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/mod.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/mod.rs @@ -76,7 +76,6 @@ impl Def { pub fn try_from(mut item: syn::ItemMod, dev_mode: bool) -> syn::Result { let frame_system = generate_access_from_frame_or_crate("frame-system")?; let frame_support = generate_access_from_frame_or_crate("frame-support")?; - let item_span = item.span(); let items = &mut item .content @@ -221,7 +220,7 @@ impl Def { genesis_config.as_ref().map_or("unused", |_| "used"), genesis_build.as_ref().map_or("unused", |_| "used"), ); - return Err(syn::Error::new(item_span, msg)) + return Err(syn::Error::new(item_span, msg)); } Self::resolve_tasks(&item_span, &mut tasks, &mut task_enum, items)?; @@ -284,7 +283,7 @@ impl Def { tasks.item_impl.impl_token.span(), "A `#[pallet::tasks_experimental]` attribute must be attached to your `Task` impl if the \ task enum has been omitted", - )) + )); } else { }, _ => (), @@ -313,7 +312,7 @@ impl Def { // `task_enum`. We use a no-op instead of simply removing it from the vec // so that any indices collected by `Def::try_from` remain accurate *item = syn::Item::Verbatim(quote::quote!()); - break + break; } } *task_enum = result; @@ -336,7 +335,7 @@ impl Def { let syn::Type::Path(target_path) = &*item_impl.self_ty else { continue }; let target_path = target_path.path.segments.iter().collect::>(); let (Some(target_ident), None) = (target_path.get(0), target_path.get(1)) else { - continue + continue; }; let matches_task_enum = match task_enum { Some(task_enum) => task_enum.item_enum.ident == target_ident.ident, @@ -344,7 +343,7 @@ impl Def { }; if trait_last_seg.ident == "Task" && matches_task_enum { result = Some(syn::parse2::(item_impl.to_token_stream())?); - break + break; } } *tasks = result; @@ -407,7 +406,7 @@ impl Def { let mut errors = instances.into_iter().filter_map(|instances| { if instances.has_instance == self.config.has_instance { - return None + return None; } let msg = if self.config.has_instance { "Invalid generic declaration, trait is defined with instance but generic use none" @@ -739,7 +738,7 @@ impl syn::parse::Parse for InheritedCallWeightAttr { content.parse::().expect("peeked"); content } else { - return Err(lookahead.error()) + return Err(lookahead.error()); }; Ok(Self { typename: buffer.parse()? }) diff --git a/substrate/frame/support/procedural/src/pallet/parse/storage.rs b/substrate/frame/support/procedural/src/pallet/parse/storage.rs index 9d96a18b5694..352f691151be 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/storage.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/storage.rs @@ -207,6 +207,8 @@ pub struct StorageDef { pub try_decode: bool, /// Whether or not a default hasher is allowed to replace `_` pub use_default_hasher: bool, + /// Attributes + pub attrs: Vec, } /// The parsed generic from the @@ -373,7 +375,7 @@ fn process_named_generics( let msg = "Invalid pallet::storage, Duplicated named generic"; let mut err = syn::Error::new(arg.ident.span(), msg); err.combine(syn::Error::new(other.ident.span(), msg)); - return Err(err) + return Err(err); } parsed.insert(arg.ident.to_string(), arg.clone()); } @@ -666,7 +668,7 @@ fn process_generics( in order to expand metadata, found `{}`.", found, ); - return Err(syn::Error::new(segment.ident.span(), msg)) + return Err(syn::Error::new(segment.ident.span(), msg)); }, }; @@ -677,7 +679,7 @@ fn process_generics( _ => { let msg = "Invalid pallet::storage, invalid number of generic generic arguments, \ expect more that 0 generic arguments."; - return Err(syn::Error::new(segment.span(), msg)) + return Err(syn::Error::new(segment.span(), msg)); }, }; @@ -724,7 +726,7 @@ fn extract_key(ty: &syn::Type) -> syn::Result { typ } else { let msg = "Invalid pallet::storage, expected type path"; - return Err(syn::Error::new(ty.span(), msg)) + return Err(syn::Error::new(ty.span(), msg)); }; let key_struct = typ.path.segments.last().ok_or_else(|| { @@ -733,14 +735,14 @@ fn extract_key(ty: &syn::Type) -> syn::Result { })?; if key_struct.ident != "Key" && key_struct.ident != "NMapKey" { let msg = "Invalid pallet::storage, expected Key or NMapKey struct"; - return Err(syn::Error::new(key_struct.ident.span(), msg)) + return Err(syn::Error::new(key_struct.ident.span(), msg)); } let ty_params = if let syn::PathArguments::AngleBracketed(args) = &key_struct.arguments { args } else { let msg = "Invalid pallet::storage, expected angle bracketed arguments"; - return Err(syn::Error::new(key_struct.arguments.span(), msg)) + return Err(syn::Error::new(key_struct.arguments.span(), msg)); }; if ty_params.args.len() != 2 { @@ -749,14 +751,14 @@ fn extract_key(ty: &syn::Type) -> syn::Result { for Key struct, expected 2 args, found {}", ty_params.args.len() ); - return Err(syn::Error::new(ty_params.span(), msg)) + return Err(syn::Error::new(ty_params.span(), msg)); } let key = match &ty_params.args[1] { syn::GenericArgument::Type(key_ty) => key_ty.clone(), _ => { let msg = "Invalid pallet::storage, expected type"; - return Err(syn::Error::new(ty_params.args[1].span(), msg)) + return Err(syn::Error::new(ty_params.args[1].span(), msg)); }, }; @@ -790,7 +792,7 @@ impl StorageDef { let item = if let syn::Item::Type(item) = item { item } else { - return Err(syn::Error::new(item.span(), "Invalid pallet::storage, expect item type.")) + return Err(syn::Error::new(item.span(), "Invalid pallet::storage, expect item type.")); }; let attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; @@ -810,12 +812,12 @@ impl StorageDef { typ } else { let msg = "Invalid pallet::storage, expected type path"; - return Err(syn::Error::new(item.ty.span(), msg)) + return Err(syn::Error::new(item.ty.span(), msg)); }; if typ.path.segments.len() != 1 { let msg = "Invalid pallet::storage, expected type path with one segment"; - return Err(syn::Error::new(item.ty.span(), msg)) + return Err(syn::Error::new(item.ty.span(), msg)); } let (named_generics, metadata, query_kind, use_default_hasher) = @@ -858,7 +860,7 @@ impl StorageDef { for ResultQuery, expected 1 type argument, found {}", args.len(), ); - return Err(syn::Error::new(args.span(), msg)) + return Err(syn::Error::new(args.span(), msg)); } args[0].clone() @@ -869,7 +871,7 @@ impl StorageDef { expected angle-bracketed arguments, found `{}`", args.to_token_stream().to_string() ); - return Err(syn::Error::new(args.span(), msg)) + return Err(syn::Error::new(args.span(), msg)); }, }; @@ -885,7 +887,7 @@ impl StorageDef { segments, found {}", err_variant.len(), ); - return Err(syn::Error::new(err_variant.span(), msg)) + return Err(syn::Error::new(err_variant.span(), msg)); } let mut error = err_variant.clone(); let err_variant = error @@ -921,7 +923,7 @@ impl StorageDef { let msg = "Invalid pallet::storage, cannot generate getter because QueryKind is not \ identifiable. QueryKind must be `OptionQuery`, `ResultQuery`, `ValueQuery`, or default \ one to be identifiable."; - return Err(syn::Error::new(getter.span(), msg)) + return Err(syn::Error::new(getter.span(), msg)); } Ok(StorageDef { @@ -942,6 +944,7 @@ impl StorageDef { whitelisted, try_decode, use_default_hasher, + attrs: item.attrs.clone(), }) } } diff --git a/substrate/frame/support/procedural/src/runtime/parse/mod.rs b/substrate/frame/support/procedural/src/runtime/parse/mod.rs index a3d1c9417df8..27a7c35a7300 100644 --- a/substrate/frame/support/procedural/src/runtime/parse/mod.rs +++ b/substrate/frame/support/procedural/src/runtime/parse/mod.rs @@ -87,7 +87,7 @@ impl syn::parse::Parse for RuntimeAttr { let pallet_index = pallet_index_content.parse::()?; if !pallet_index.suffix().is_empty() { let msg = "Number literal must not have a suffix"; - return Err(syn::Error::new(pallet_index.span(), msg)) + return Err(syn::Error::new(pallet_index.span(), msg)); } Ok(RuntimeAttr::PalletIndex(pallet_index.span(), pallet_index.base10_parse()?)) } else if lookahead.peek(keyword::disable_call) { @@ -171,14 +171,14 @@ impl Def { Some((index, item.clone())) } else { let msg = "Invalid runtime::pallet_index, expected type definition"; - return Err(syn::Error::new(span, msg)) + return Err(syn::Error::new(span, msg)); }; }, RuntimeAttr::DisableCall(_) => disable_call = true, RuntimeAttr::DisableUnsigned(_) => disable_unsigned = true, attr => { let msg = "Invalid duplicated attribute"; - return Err(syn::Error::new(attr.span(), msg)) + return Err(syn::Error::new(attr.span(), msg)); }, } } @@ -196,7 +196,7 @@ impl Def { let mut err = syn::Error::new(used_pallet, &msg); err.combine(syn::Error::new(pallet_decl.name.span(), &msg)); - return Err(err) + return Err(err); } pallet_decls.push(pallet_decl); @@ -219,7 +219,7 @@ impl Def { ); let mut err = syn::Error::new(used_pallet.span(), &msg); err.combine(syn::Error::new(pallet.name.span(), msg)); - return Err(err) + return Err(err); } pallets.push(pallet); @@ -229,7 +229,7 @@ impl Def { } else { if let syn::Item::Type(item) = item { let msg = "Missing pallet index for pallet declaration. Please add `#[runtime::pallet_index(...)]`"; - return Err(syn::Error::new(item.span(), &msg)) + return Err(syn::Error::new(item.span(), &msg)); } } } diff --git a/substrate/frame/support/procedural/src/runtime/parse/pallet.rs b/substrate/frame/support/procedural/src/runtime/parse/pallet.rs index ebfd0c9cccee..de1efa267c89 100644 --- a/substrate/frame/support/procedural/src/runtime/parse/pallet.rs +++ b/substrate/frame/support/procedural/src/runtime/parse/pallet.rs @@ -49,7 +49,7 @@ impl Pallet { return Err(Error::new( attr_span, "Invalid pallet declaration, expected a path or a trait object", - )) + )); }; } @@ -91,6 +91,7 @@ impl Pallet { cfg_pattern, pallet_parts, docs, + attrs: item.attrs.clone(), }) } } diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs index bd571571ee24..28283f2a5a06 100644 --- a/substrate/frame/support/src/lib.rs +++ b/substrate/frame/support/src/lib.rs @@ -1051,6 +1051,12 @@ pub mod pallet_prelude { /// [`StorageInfoTrait`](frame_support::traits::StorageInfoTrait) for the pallet using the /// [`PartialStorageInfoTrait`](frame_support::traits::PartialStorageInfoTrait) /// implementation of storages. +/// +/// ## Note on deprecation. +/// +/// - Usage of `deprecated` attribute will propagate deprecation information to the pallet +/// metadata. +/// - For general usage examples of `deprecated` attribute please refer to pub use frame_support_procedural::pallet; /// Contains macro stubs for all of the `pallet::` macros @@ -1892,6 +1898,15 @@ pub mod pallet_macros { /// /// The macro also implements `From>` for `&'static str` and `From>` for /// `DispatchError`. + /// + /// ## Note on deprecation of Errors + /// + /// - Usage of `deprecated` attribute will propagate deprecation information to the pallet + /// metadata where the item was declared. + /// - For general usage examples of `deprecated` attribute please refer to + /// - It's possible to deprecated either certain variants inside the `Error` or the whole + /// `Error` itself. If both the `Error` and its variants are deprecated a compile error + /// will be returned. pub use frame_support_procedural::error; /// Allows defining pallet events. @@ -1933,6 +1948,15 @@ pub mod pallet_macros { /// Each field must implement [`Clone`], [`Eq`], [`PartialEq`], [`codec::Encode`], /// [`codec::Decode`], and [`Debug`] (on std only). For ease of use, bound by the trait /// `Member`, available in [`frame_support::pallet_prelude`]. + /// + /// ## Note on deprecation of Events + /// + /// - Usage of `deprecated` attribute will propagate deprecation information to the pallet + /// metadata where the item was declared. + /// - For general usage examples of `deprecated` attribute please refer to + /// - It's possible to deprecated either certain variants inside the `Event` or the whole + /// `Event` itself. If both the `Event` and its variants are deprecated a compile error + /// will be returned. pub use frame_support_procedural::event; /// Allows a pallet to declare a set of functions as a *dispatchable extrinsic*. @@ -2053,6 +2077,12 @@ pub mod pallet_macros { /// pub trait Config: frame_system::Config {} /// } /// ``` + /// + /// ## Note on deprecation of Calls + /// + /// - Usage of `deprecated` attribute will propagate deprecation information to the pallet + /// metadata where the item was declared. + /// - For general usage examples of `deprecated` attribute please refer to pub use frame_support_procedural::call; /// Enforce the index of a variant in the generated `enum Call`. @@ -2185,6 +2215,12 @@ pub mod pallet_macros { /// } /// } /// ``` + /// + /// ## Note on deprecation of constants + /// + /// - Usage of `deprecated` attribute will propagate deprecation information to the pallet + /// metadata where the item was declared. + /// - For general usage examples of `deprecated` attribute please refer to pub use frame_support_procedural::constant; /// Declares a type alias as a storage item. @@ -2403,6 +2439,12 @@ pub mod pallet_macros { /// pub type Foo = StorageValue<_, u32, ValueQuery>; /// } /// ``` + /// + /// ## Note on deprecation of storage items + /// + /// - Usage of `deprecated` attribute will propagate deprecation information to the pallet + /// metadata where the storage item was declared. + /// - For general usage examples of `deprecated` attribute please refer to pub use frame_support_procedural::storage; pub use frame_support_procedural::{ diff --git a/substrate/frame/support/src/storage/generator/map.rs b/substrate/frame/support/src/storage/generator/map.rs index 7a74308fdeac..e905df41a5a6 100644 --- a/substrate/frame/support/src/storage/generator/map.rs +++ b/substrate/frame/support/src/storage/generator/map.rs @@ -136,7 +136,7 @@ where loop { previous_key = Self::translate_next(previous_key, &mut f); if previous_key.is_none() { - break + break; } } } @@ -158,7 +158,7 @@ where "Invalid translation: failed to decode old value for key", array_bytes::bytes2hex("0x", ¤t_key) ); - return Some(current_key) + return Some(current_key); }, }; @@ -170,7 +170,7 @@ where "Invalid translation: failed to decode key", array_bytes::bytes2hex("0x", ¤t_key) ); - return Some(current_key) + return Some(current_key); }, }; diff --git a/substrate/frame/support/src/storage/types/counted_map.rs b/substrate/frame/support/src/storage/types/counted_map.rs index 9adcb33ae074..ae155eab5dd7 100644 --- a/substrate/frame/support/src/storage/types/counted_map.rs +++ b/substrate/frame/support/src/storage/types/counted_map.rs @@ -508,9 +508,14 @@ where OnEmpty: Get + 'static, MaxValues: Get>, { - fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { - ::Map::build_metadata(docs, entries); + fn build_metadata( + deprecation_status: sp_metadata_ir::DeprecationStatusIR, + docs: Vec<&'static str>, + entries: &mut Vec, + ) { + ::Map::build_metadata(deprecation_status.clone(), docs, entries); CounterFor::::build_metadata( + deprecation_status, if cfg!(feature = "no-metadata-docs") { vec![] } else { @@ -1193,7 +1198,7 @@ mod test { fn test_metadata() { type A = CountedStorageMap; let mut entries = vec![]; - A::build_metadata(vec![], &mut entries); + A::build_metadata(sp_metadata_ir::DeprecationStatusIR::NotDeprecated, vec![], &mut entries); assert_eq!( entries, vec![ @@ -1207,6 +1212,7 @@ mod test { }, default: 97u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "counter_for_foo", @@ -1218,6 +1224,7 @@ mod test { } else { vec!["Counter for the related counted storage map"] }, + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, ] ); diff --git a/substrate/frame/support/src/storage/types/counted_nmap.rs b/substrate/frame/support/src/storage/types/counted_nmap.rs index 13c1b10be39c..45bd82ffe7e5 100644 --- a/substrate/frame/support/src/storage/types/counted_nmap.rs +++ b/substrate/frame/support/src/storage/types/counted_nmap.rs @@ -632,9 +632,14 @@ where OnEmpty: Get + 'static, MaxValues: Get>, { - fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { - ::Map::build_metadata(docs, entries); + fn build_metadata( + deprecation_status: sp_metadata_ir::DeprecationStatusIR, + docs: Vec<&'static str>, + entries: &mut Vec, + ) { + ::Map::build_metadata(deprecation_status.clone(), docs, entries); CounterFor::::build_metadata( + deprecation_status, vec![&"Counter for the related counted storage map"], entries, ); @@ -859,8 +864,16 @@ mod test { assert_eq!(A::count(), 2); let mut entries = vec![]; - A::build_metadata(vec![], &mut entries); - AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries); + A::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); + AValueQueryWithAnOnEmpty::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); assert_eq!( entries, vec![ @@ -874,6 +887,7 @@ mod test { }, default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -885,6 +899,7 @@ mod test { } else { vec!["Counter for the related counted storage map"] }, + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -896,6 +911,7 @@ mod test { }, default: 98u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -907,6 +923,7 @@ mod test { } else { vec!["Counter for the related counted storage map"] }, + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, ] ); @@ -1108,8 +1125,16 @@ mod test { assert_eq!(A::count(), 2); let mut entries = vec![]; - A::build_metadata(vec![], &mut entries); - AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries); + A::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); + AValueQueryWithAnOnEmpty::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); assert_eq!( entries, vec![ @@ -1126,6 +1151,7 @@ mod test { }, default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -1137,6 +1163,7 @@ mod test { } else { vec!["Counter for the related counted storage map"] }, + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -1151,6 +1178,7 @@ mod test { }, default: 98u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -1162,6 +1190,7 @@ mod test { } else { vec!["Counter for the related counted storage map"] }, + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, ] ); @@ -1394,8 +1423,16 @@ mod test { assert_eq!(A::count(), 2); let mut entries = vec![]; - A::build_metadata(vec![], &mut entries); - AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries); + A::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); + AValueQueryWithAnOnEmpty::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); assert_eq!( entries, vec![ @@ -1413,6 +1450,7 @@ mod test { }, default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -1424,6 +1462,7 @@ mod test { } else { vec!["Counter for the related counted storage map"] }, + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -1439,6 +1478,7 @@ mod test { }, default: 98u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -1450,6 +1490,7 @@ mod test { } else { vec!["Counter for the related counted storage map"] }, + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, ] ); diff --git a/substrate/frame/support/src/storage/types/double_map.rs b/substrate/frame/support/src/storage/types/double_map.rs index 3d227feb902f..c70d9de54467 100644 --- a/substrate/frame/support/src/storage/types/double_map.rs +++ b/substrate/frame/support/src/storage/types/double_map.rs @@ -732,7 +732,11 @@ where OnEmpty: Get + 'static, MaxValues: Get>, { - fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { + fn build_metadata( + deprecation_status: sp_metadata_ir::DeprecationStatusIR, + docs: Vec<&'static str>, + entries: &mut Vec, + ) { let docs = if cfg!(feature = "no-metadata-docs") { vec![] } else { docs }; let entry = StorageEntryMetadataIR { @@ -745,6 +749,7 @@ where }, default: OnEmpty::get().encode(), docs, + deprecation_info: deprecation_status, }; entries.push(entry); @@ -985,8 +990,16 @@ mod test { assert_eq!(A::iter().collect::>(), vec![(4, 40, 1600), (3, 30, 900)]); let mut entries = vec![]; - A::build_metadata(vec![], &mut entries); - AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries); + A::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); + AValueQueryWithAnOnEmpty::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); assert_eq!( entries, vec![ @@ -1003,6 +1016,7 @@ mod test { }, default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated }, StorageEntryMetadataIR { name: "foo", @@ -1017,6 +1031,7 @@ mod test { }, default: 97u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated } ] ); diff --git a/substrate/frame/support/src/storage/types/map.rs b/substrate/frame/support/src/storage/types/map.rs index b70026eea50e..3582448c7dd5 100644 --- a/substrate/frame/support/src/storage/types/map.rs +++ b/substrate/frame/support/src/storage/types/map.rs @@ -490,7 +490,11 @@ where OnEmpty: Get + 'static, MaxValues: Get>, { - fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { + fn build_metadata( + deprecation_status: sp_metadata_ir::DeprecationStatusIR, + docs: Vec<&'static str>, + entries: &mut Vec, + ) { let docs = if cfg!(feature = "no-metadata-docs") { vec![] } else { docs }; let entry = StorageEntryMetadataIR { @@ -503,6 +507,7 @@ where }, default: OnEmpty::get().encode(), docs, + deprecation_info: deprecation_status, }; entries.push(entry); @@ -791,8 +796,16 @@ mod test { assert_eq!(A::iter().collect::>(), vec![(3, 10)]); let mut entries = vec![]; - A::build_metadata(vec![], &mut entries); - AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries); + A::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); + AValueQueryWithAnOnEmpty::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); assert_eq!( entries, vec![ @@ -806,6 +819,7 @@ mod test { }, default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated }, StorageEntryMetadataIR { name: "foo", @@ -817,6 +831,7 @@ mod test { }, default: 97u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated } ] ); diff --git a/substrate/frame/support/src/storage/types/mod.rs b/substrate/frame/support/src/storage/types/mod.rs index b063e11621d6..b1c8a58fc8fb 100644 --- a/substrate/frame/support/src/storage/types/mod.rs +++ b/substrate/frame/support/src/storage/types/mod.rs @@ -141,7 +141,11 @@ where /// Implemented by each of the storage types: value, map, countedmap, doublemap and nmap. pub trait StorageEntryMetadataBuilder { /// Build into `entries` the storage metadata entries of a storage given some `docs`. - fn build_metadata(doc: Vec<&'static str>, entries: &mut Vec); + fn build_metadata( + deprecation_status: sp_metadata_ir::DeprecationStatusIR, + doc: Vec<&'static str>, + entries: &mut Vec, + ); } #[cfg(test)] diff --git a/substrate/frame/support/src/storage/types/nmap.rs b/substrate/frame/support/src/storage/types/nmap.rs index c3dfd5b3e48c..9ee012f86286 100755 --- a/substrate/frame/support/src/storage/types/nmap.rs +++ b/substrate/frame/support/src/storage/types/nmap.rs @@ -583,7 +583,11 @@ where OnEmpty: Get + 'static, MaxValues: Get>, { - fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { + fn build_metadata( + deprecation_status: sp_metadata_ir::DeprecationStatusIR, + docs: Vec<&'static str>, + entries: &mut Vec, + ) { let docs = if cfg!(feature = "no-metadata-docs") { vec![] } else { docs }; let entry = StorageEntryMetadataIR { @@ -596,6 +600,7 @@ where }, default: OnEmpty::get().encode(), docs, + deprecation_info: deprecation_status, }; entries.push(entry); @@ -820,8 +825,16 @@ mod test { assert_eq!(A::iter().collect::>(), vec![(4, 40), (3, 30)]); let mut entries = vec![]; - A::build_metadata(vec![], &mut entries); - AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries); + A::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); + AValueQueryWithAnOnEmpty::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); assert_eq!( entries, vec![ @@ -835,6 +848,7 @@ mod test { }, default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -846,6 +860,7 @@ mod test { }, default: 98u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, } ] ); @@ -1020,8 +1035,16 @@ mod test { assert_eq!(A::iter().collect::>(), vec![((4, 40), 1600), ((3, 30), 900)]); let mut entries = vec![]; - A::build_metadata(vec![], &mut entries); - AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries); + A::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); + AValueQueryWithAnOnEmpty::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); assert_eq!( entries, vec![ @@ -1038,6 +1061,7 @@ mod test { }, default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -1052,6 +1076,7 @@ mod test { }, default: 98u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, } ] ); @@ -1261,8 +1286,16 @@ mod test { assert_eq!(A::iter().collect::>(), vec![((4, 40, 400), 4), ((3, 30, 300), 3)]); let mut entries = vec![]; - A::build_metadata(vec![], &mut entries); - AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries); + A::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); + AValueQueryWithAnOnEmpty::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); assert_eq!( entries, vec![ @@ -1280,6 +1313,7 @@ mod test { }, default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -1295,6 +1329,7 @@ mod test { }, default: 98u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, } ] ); diff --git a/substrate/frame/support/src/storage/types/value.rs b/substrate/frame/support/src/storage/types/value.rs index 9cc985b36d8c..69a8f3bb4b8c 100644 --- a/substrate/frame/support/src/storage/types/value.rs +++ b/substrate/frame/support/src/storage/types/value.rs @@ -277,7 +277,11 @@ where QueryKind: QueryKindTrait, OnEmpty: crate::traits::Get + 'static, { - fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { + fn build_metadata( + deprecation_status: sp_metadata_ir::DeprecationStatusIR, + docs: Vec<&'static str>, + entries: &mut Vec, + ) { let docs = if cfg!(feature = "no-metadata-docs") { vec![] } else { docs }; let entry = StorageEntryMetadataIR { @@ -286,6 +290,7 @@ where ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: OnEmpty::get().encode(), docs, + deprecation_info: deprecation_status, }; entries.push(entry); @@ -415,8 +420,16 @@ mod test { assert_eq!(A::try_get(), Err(())); let mut entries = vec![]; - A::build_metadata(vec![], &mut entries); - AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries); + A::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); + AValueQueryWithAnOnEmpty::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); assert_eq!( entries, vec![ @@ -426,6 +439,7 @@ mod test { ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated }, StorageEntryMetadataIR { name: "foo", @@ -433,6 +447,7 @@ mod test { ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: 97u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated } ] ); diff --git a/substrate/frame/support/src/tests/mod.rs b/substrate/frame/support/src/tests/mod.rs index a9f8ebdd63f2..5e1bcc777df4 100644 --- a/substrate/frame/support/src/tests/mod.rs +++ b/substrate/frame/support/src/tests/mod.rs @@ -14,6 +14,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +#![allow(deprecated, clippy::deprecated_semver)] use super::*; use sp_io::{MultiRemovalResults, TestExternalities}; @@ -74,6 +75,10 @@ pub mod frame_system { #[pallet::no_default_bounds] type PalletInfo: crate::traits::PalletInfo; type DbWeight: Get; + #[pallet::constant] + #[pallet::no_default] + #[deprecated = "this constant is deprecated"] + type ExampleConstant: Get<()>; } #[pallet::error] @@ -97,11 +102,11 @@ pub mod frame_system { #[pallet::weight(task.weight())] pub fn do_task(_origin: OriginFor, task: T::RuntimeTask) -> DispatchResultWithPostInfo { if !task.is_valid() { - return Err(Error::::InvalidTask.into()) + return Err(Error::::InvalidTask.into()); } if let Err(_err) = task.run() { - return Err(Error::::FailedTask.into()) + return Err(Error::::FailedTask.into()); } Ok(().into()) @@ -109,18 +114,22 @@ pub mod frame_system { } #[pallet::storage] + #[deprecated] pub type Data = StorageMap<_, Twox64Concat, u32, u64, ValueQuery>; #[pallet::storage] + #[deprecated(note = "test")] pub type OptionLinkedMap = StorageMap<_, Blake2_128Concat, u32, u32, OptionQuery>; #[pallet::storage] #[pallet::getter(fn generic_data)] + #[deprecated(note = "test", since = "test")] pub type GenericData = StorageMap<_, Identity, BlockNumberFor, BlockNumberFor, ValueQuery>; #[pallet::storage] #[pallet::getter(fn generic_data2)] + #[deprecated = "test"] pub type GenericData2 = StorageMap<_, Blake2_128Concat, BlockNumberFor, BlockNumberFor, OptionQuery>; @@ -240,6 +249,7 @@ mod runtime { impl Config for Runtime { type Block = Block; type AccountId = AccountId; + type ExampleConstant = (); } fn new_test_ext() -> TestExternalities { @@ -585,6 +595,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: vec![0, 0, 0, 0, 0, 0, 0, 0], docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::DeprecatedWithoutNote, }, StorageEntryMetadataIR { name: "OptionLinkedMap", @@ -596,6 +607,10 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: vec![0], docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::Deprecated { + note: "test", + since: None, + }, }, StorageEntryMetadataIR { name: "GenericData", @@ -607,6 +622,10 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: vec![0, 0, 0, 0], docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::Deprecated { + note: "test", + since: Some("test"), + }, }, StorageEntryMetadataIR { name: "GenericData2", @@ -618,6 +637,10 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: vec![0], docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::Deprecated { + note: "test", + since: None, + }, }, StorageEntryMetadataIR { name: "DataDM", @@ -629,6 +652,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: vec![0, 0, 0, 0, 0, 0, 0, 0], docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "GenericDataDM", @@ -640,6 +664,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: vec![0, 0, 0, 0], docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "GenericData2DM", @@ -651,6 +676,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: vec![0], docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "AppendableDM", @@ -665,6 +691,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: vec![0], docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Total", @@ -672,6 +699,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { ty: StorageEntryTypeIR::Plain(scale_info::meta_type::<(u32, u32)>()), default: vec![0, 0, 0, 0, 0, 0, 0, 0], docs: vec![" Some running total."], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Numbers", @@ -683,6 +711,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: vec![0], docs: vec![" Numbers to be added into the total."], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, ], } @@ -694,6 +723,25 @@ fn store_metadata() { pretty_assertions::assert_eq!(expected_metadata(), metadata); } +#[test] +fn constant_metadata() { + let metadata: Vec = + Pallet::::pallet_constants_metadata(); + pretty_assertions::assert_eq!( + metadata, + vec![sp_metadata_ir::PalletConstantMetadataIR { + name: "ExampleConstant", + ty: scale_info::meta_type::<()>(), + value: vec![], + docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::Deprecated { + note: "this constant is deprecated", + since: None + } + },] + ); +} + parameter_types! { storage StorageParameter: u64 = 10; } diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/undefined_event_part.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/undefined_event_part.stderr index 6f412fe89eab..0f7afb2b9901 100644 --- a/substrate/frame/support/test/tests/construct_runtime_ui/undefined_event_part.stderr +++ b/substrate/frame/support/test/tests/construct_runtime_ui/undefined_event_part.stderr @@ -34,3 +34,23 @@ help: consider importing one of these items | 18 + use frame_system::Event; | + +error[E0433]: failed to resolve: could not find `Event` in `pallet` + --> tests/construct_runtime_ui/undefined_event_part.rs:66:1 + | +66 | / construct_runtime! { +67 | | pub struct Runtime +68 | | { +69 | | System: frame_system expanded::{}::{Pallet, Call, Storage, Config, Event}, +70 | | Pallet: pallet expanded::{}::{Pallet, Event}, +71 | | } +72 | | } + | |_^ could not find `Event` in `pallet` + | + = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing one of these items + | +18 + use frame_support_test::Event; + | +18 + use frame_system::Event; + | diff --git a/substrate/frame/support/test/tests/enum_deprecation.rs b/substrate/frame/support/test/tests/enum_deprecation.rs new file mode 100644 index 000000000000..ed9b2b5a735d --- /dev/null +++ b/substrate/frame/support/test/tests/enum_deprecation.rs @@ -0,0 +1,173 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#![allow(useless_deprecated, deprecated, clippy::deprecated_semver)] + +use std::collections::BTreeMap; + +use frame_support::{ + derive_impl, + dispatch::Parameter, + parameter_types, + traits::{ConstU32, StorageVersion}, + OrdNoBound, PartialOrdNoBound, +}; +use scale_info::TypeInfo; + +parameter_types! { + /// Used to control if the storage version should be updated. + storage UpdateStorageVersion: bool = false; +} + +pub struct SomeType1; +impl From for u64 { + fn from(_t: SomeType1) -> Self { + 0u64 + } +} + +pub trait SomeAssociation1 { + type _1: Parameter + codec::MaxEncodedLen + TypeInfo; +} +impl SomeAssociation1 for u64 { + type _1 = u64; +} + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + + pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(10); + + #[pallet::config] + pub trait Config: frame_system::Config + where + ::AccountId: From + SomeAssociation1, + { + type Balance: Parameter + Default + TypeInfo; + + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(_); + + #[pallet::error] + #[derive(PartialEq, Eq)] + pub enum Error { + /// error doc comment put in metadata + InsufficientProposersBalance, + NonExistentStorageValue, + Code(u8), + #[codec(skip)] + Skipped(u128), + CompactU8(#[codec(compact)] u8), + } + + #[pallet::event] + pub enum Event + where + T::AccountId: SomeAssociation1 + From, + { + #[deprecated = "second"] + A, + #[deprecated = "first"] + #[codec(index = 0)] + B, + } + + #[pallet::origin] + #[derive( + EqNoBound, + RuntimeDebugNoBound, + CloneNoBound, + PartialEqNoBound, + PartialOrdNoBound, + OrdNoBound, + Encode, + Decode, + TypeInfo, + MaxEncodedLen, + )] + pub struct Origin(PhantomData); +} + +frame_support::parameter_types!( + pub const MyGetParam3: u32 = 12; +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Runtime { + type BaseCallFilter = frame_support::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type Nonce = u64; + type RuntimeCall = RuntimeCall; + type Hash = sp_runtime::testing::H256; + type Hashing = sp_runtime::traits::BlakeTwo256; + type AccountId = u64; + type Lookup = sp_runtime::traits::IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type MaxConsumers = ConstU32<16>; +} +impl pallet::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = u64; +} + +pub type Header = sp_runtime::generic::Header; +pub type Block = sp_runtime::generic::Block; +pub type UncheckedExtrinsic = + sp_runtime::testing::TestXt>; + +frame_support::construct_runtime!( + pub struct Runtime { + // Exclude part `Storage` in order not to check its metadata in tests. + System: frame_system exclude_parts { Pallet, Storage }, + Example: pallet, + + } +); + +#[test] +fn pallet_metadata() { + use sp_metadata_ir::{DeprecationInfoIR, DeprecationStatusIR}; + let pallets = Runtime::metadata_ir().pallets; + let example = pallets[0].clone(); + { + // Example pallet events are partially and fully deprecated + let meta = example.event.unwrap(); + assert_eq!( + // Result should be this, but instead we get the result below + // see: https://github.com/paritytech/parity-scale-codec/issues/507 + // + // DeprecationInfoIR::VariantsDeprecated(BTreeMap::from([ + // (codec::Compact(0), DeprecationStatusIR::Deprecated { note: "first", since: None + // }), ( + // codec::Compact(1), + // DeprecationStatusIR::Deprecated { note: "second", since: None } + // ) + // ])), + DeprecationInfoIR::VariantsDeprecated(BTreeMap::from([( + codec::Compact(0), + DeprecationStatusIR::Deprecated { note: "first", since: None } + ),])), + meta.deprecation_info + ); + } +} diff --git a/substrate/frame/support/test/tests/instance.rs b/substrate/frame/support/test/tests/instance.rs index 30b8338bc5c7..7f8423a0127e 100644 --- a/substrate/frame/support/test/tests/instance.rs +++ b/substrate/frame/support/test/tests/instance.rs @@ -441,6 +441,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![0, 0, 0, 0], docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Map", @@ -452,6 +453,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: [0u8; 8].to_vec(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "DoubleMap", @@ -463,6 +465,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: [0u8; 8].to_vec(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, ], } diff --git a/substrate/frame/support/test/tests/pallet.rs b/substrate/frame/support/test/tests/pallet.rs index eed8a22e8e79..7f1ce0556eab 100644 --- a/substrate/frame/support/test/tests/pallet.rs +++ b/substrate/frame/support/test/tests/pallet.rs @@ -14,6 +14,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +#![allow(useless_deprecated, deprecated, clippy::deprecated_semver)] + +use std::collections::BTreeMap; use frame_support::{ assert_ok, derive_impl, @@ -212,6 +215,7 @@ pub mod pallet { /// call foo doc comment put in metadata #[pallet::call_index(0)] #[pallet::weight(Weight::from_parts(*foo as u64, 0))] + #[deprecated = "test"] pub fn foo( origin: OriginFor, #[pallet::compact] foo: u32, @@ -272,6 +276,7 @@ pub mod pallet { pub enum Error { /// error doc comment put in metadata InsufficientProposersBalance, + #[deprecated = "test"] NonExistentStorageValue, Code(u8), #[codec(skip)] @@ -283,6 +288,7 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(fn deposit_event)] + #[deprecated = "test"] pub enum Event where T::AccountId: SomeAssociation1 + From, @@ -486,7 +492,7 @@ pub mod pallet { let _ = T::AccountId::from(SomeType1); // Test for where clause let _ = T::AccountId::from(SomeType5); // Test for where clause if matches!(call, Call::foo_storage_layer { .. }) { - return Ok(ValidTransaction::default()) + return Ok(ValidTransaction::default()); } Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) } @@ -553,6 +559,7 @@ pub mod pallet { // Test that a pallet with non generic event and generic genesis_config is correctly handled // and that a pallet with the attribute without_storage_info is correctly handled. #[frame_support::pallet] +#[deprecated = "test"] pub mod pallet2 { use super::{SomeAssociation1, SomeType1, UpdateStorageVersion}; use frame_support::pallet_prelude::*; @@ -2478,3 +2485,56 @@ fn test_error_feature_parsing() { pallet::Error::__Ignore(_, _) => (), } } + +#[test] +fn pallet_metadata() { + use sp_metadata_ir::{DeprecationInfoIR, DeprecationStatusIR}; + let pallets = Runtime::metadata_ir().pallets; + let example = pallets[0].clone(); + let example2 = pallets[1].clone(); + { + // Example2 pallet is deprecated + assert_eq!( + &DeprecationStatusIR::Deprecated { note: "test", since: None }, + &example2.deprecation_info + ) + } + { + // Example pallet calls is fully and partially deprecated + let meta = &example.calls.unwrap(); + assert_eq!( + DeprecationInfoIR::VariantsDeprecated(BTreeMap::from([( + codec::Compact(0), + DeprecationStatusIR::Deprecated { note: "test", since: None } + )])), + meta.deprecation_info + ) + } + { + // Example pallet errors are partially and fully deprecated + let meta = &example.error.unwrap(); + assert_eq!( + DeprecationInfoIR::VariantsDeprecated(BTreeMap::from([( + codec::Compact(2), + DeprecationStatusIR::Deprecated { note: "test", since: None } + )])), + meta.deprecation_info + ) + } + { + // Example pallet events are partially and fully deprecated + let meta = example.event.unwrap(); + assert_eq!( + DeprecationInfoIR::ItemDeprecated(DeprecationStatusIR::Deprecated { + note: "test", + since: None + }), + meta.deprecation_info + ); + } + { + // Example2 pallet events are not deprecated + let meta = example2.event.unwrap(); + assert_eq!(DeprecationInfoIR::NotDeprecated, meta.deprecation_info); + } +} diff --git a/substrate/frame/support/test/tests/runtime_metadata.rs b/substrate/frame/support/test/tests/runtime_metadata.rs index 48e4d975eb08..81377210eb43 100644 --- a/substrate/frame/support/test/tests/runtime_metadata.rs +++ b/substrate/frame/support/test/tests/runtime_metadata.rs @@ -14,11 +14,13 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +#![allow(useless_deprecated, deprecated, clippy::deprecated_semver)] use frame_support::{derive_impl, traits::ConstU32}; use scale_info::{form::MetaForm, meta_type}; use sp_metadata_ir::{ - RuntimeApiMetadataIR, RuntimeApiMethodMetadataIR, RuntimeApiMethodParamMetadataIR, + DeprecationStatusIR, RuntimeApiMetadataIR, RuntimeApiMethodMetadataIR, + RuntimeApiMethodParamMetadataIR, }; use sp_runtime::traits::Block as BlockT; @@ -64,12 +66,16 @@ sp_api::decl_runtime_apis! { /// ApiWithCustomVersion trait documentation /// /// Documentation on multiline. + #[deprecated] pub trait Api { fn test(data: u64); /// something_with_block. fn something_with_block(block: Block) -> Block; + #[deprecated = "example"] fn function_with_two_args(data: u64, block: Block); + #[deprecated(note = "example", since = "example")] fn same_name(); + #[deprecated(note = "example")] fn wild_card(_: u32); } } @@ -128,6 +134,7 @@ fn runtime_metadata() { }], output: meta_type::<()>(), docs: vec![], + deprecation_info: DeprecationStatusIR::NotDeprecated, }, RuntimeApiMethodMetadataIR { name: "something_with_block", @@ -137,6 +144,7 @@ fn runtime_metadata() { }], output: meta_type::(), docs: maybe_docs(vec![" something_with_block."]), + deprecation_info: DeprecationStatusIR::NotDeprecated, }, RuntimeApiMethodMetadataIR { name: "function_with_two_args", @@ -152,13 +160,21 @@ fn runtime_metadata() { ], output: meta_type::<()>(), docs: vec![], + deprecation_info: DeprecationStatusIR::Deprecated { + note: "example", + since: None, + } }, RuntimeApiMethodMetadataIR { name: "same_name", inputs: vec![], output: meta_type::<()>(), docs: vec![], - }, + deprecation_info: DeprecationStatusIR::Deprecated { + note: "example", + since: Some("example"), + } + }, RuntimeApiMethodMetadataIR { name: "wild_card", inputs: vec![RuntimeApiMethodParamMetadataIR:: { @@ -167,6 +183,10 @@ fn runtime_metadata() { }], output: meta_type::<()>(), docs: vec![], + deprecation_info: DeprecationStatusIR::Deprecated { + note: "example", + since: None, + } }, ], docs: maybe_docs(vec![ @@ -174,6 +194,8 @@ fn runtime_metadata() { "", " Documentation on multiline.", ]), + deprecation_info: DeprecationStatusIR::DeprecatedWithoutNote, + }, RuntimeApiMetadataIR { name: "Core", @@ -183,6 +205,7 @@ fn runtime_metadata() { inputs: vec![], output: meta_type::(), docs: maybe_docs(vec![" Returns the version of the runtime."]), + deprecation_info: DeprecationStatusIR::NotDeprecated, }, RuntimeApiMethodMetadataIR { name: "execute_block", @@ -192,6 +215,8 @@ fn runtime_metadata() { }], output: meta_type::<()>(), docs: maybe_docs(vec![" Execute the given block."]), + deprecation_info: DeprecationStatusIR::NotDeprecated, + }, RuntimeApiMethodMetadataIR { name: "initialize_block", @@ -201,11 +226,13 @@ fn runtime_metadata() { }], output: meta_type::(), docs: maybe_docs(vec![" Initialize a block with the given header and return the runtime executive mode."]), + deprecation_info: DeprecationStatusIR::NotDeprecated, }, ], docs: maybe_docs(vec![ " The `Core` runtime api that every Substrate runtime needs to implement.", ]), + deprecation_info: DeprecationStatusIR::NotDeprecated, }, ]; diff --git a/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs b/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs index 2c423f8c28dd..21397abc8fc6 100644 --- a/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs +++ b/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs @@ -471,7 +471,7 @@ fn extend_with_api_version(mut trait_: Path, version: Option) -> Path { v } else { // nothing to do - return trait_ + return trait_; }; let trait_name = &mut trait_ @@ -762,7 +762,7 @@ fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result { error.combine(Error::new(other_span, "First trait implementation.")); - return Err(error) + return Err(error); } let id: Path = parse_quote!( #path ID ); @@ -892,7 +892,7 @@ fn extract_cfg_api_version(attrs: &Vec, span: Span) -> Result, span: Span) -> Result Each runtime API can have only one version.", API_VERSION_ATTRIBUTE ), - )) + )); } // Parse the runtime version if there exists one. diff --git a/substrate/primitives/api/proc-macro/src/runtime_metadata.rs b/substrate/primitives/api/proc-macro/src/runtime_metadata.rs index 9944927d5573..4cba524dbe25 100644 --- a/substrate/primitives/api/proc-macro/src/runtime_metadata.rs +++ b/substrate/primitives/api/proc-macro/src/runtime_metadata.rs @@ -94,7 +94,7 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { let is_changed_in = method.attrs.iter().any(|attr| attr.path().is_ident(CHANGED_IN_ATTRIBUTE)); if is_changed_in { - continue + continue; } let mut inputs = Vec::new(); @@ -131,6 +131,10 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { // Include the method metadata only if its `cfg` features are enabled. let attrs = filter_cfg_attributes(&method.attrs); + let deprecation = match crate::utils::get_deprecation(&crate_, &method.attrs) { + Ok(deprecation) => deprecation, + Err(e) => return e.into_compile_error(), + }; methods.push(quote!( #( #attrs )* #crate_::metadata_ir::RuntimeApiMethodMetadataIR { @@ -138,6 +142,7 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { inputs: #crate_::vec![ #( #inputs, )* ], output: #output, docs: #docs, + deprecation_info: #deprecation, } )); } @@ -145,6 +150,10 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { let trait_name_ident = &decl.ident; let trait_name = trait_name_ident.to_string(); let docs = collect_docs(&decl.attrs, &crate_); + let deprecation = match crate::utils::get_deprecation(&crate_, &decl.attrs) { + Ok(deprecation) => deprecation, + Err(e) => return e.into_compile_error(), + }; let attrs = filter_cfg_attributes(&decl.attrs); // The trait generics where already extended with `Block: BlockT`. let mut generics = decl.generics.clone(); @@ -174,6 +183,7 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { name: #trait_name, methods: #crate_::vec![ #( #methods, )* ], docs: #docs, + deprecation_info: #deprecation, } } } @@ -187,7 +197,7 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { /// exposed by `generate_decl_runtime_metadata`. pub fn generate_impl_runtime_metadata(impls: &[ItemImpl]) -> Result { if impls.is_empty() { - return Ok(quote!()) + return Ok(quote!()); } let crate_ = generate_crate_access(); diff --git a/substrate/primitives/api/proc-macro/src/utils.rs b/substrate/primitives/api/proc-macro/src/utils.rs index 36577670a40c..94da6748cbdb 100644 --- a/substrate/primitives/api/proc-macro/src/utils.rs +++ b/substrate/primitives/api/proc-macro/src/utils.rs @@ -21,8 +21,9 @@ use proc_macro2::{Span, TokenStream}; use proc_macro_crate::{crate_name, FoundCrate}; use quote::{format_ident, quote}; use syn::{ - parse_quote, spanned::Spanned, token::And, Attribute, Error, FnArg, GenericArgument, Ident, - ImplItem, ItemImpl, Pat, Path, PathArguments, Result, ReturnType, Signature, Type, TypePath, + parse_quote, punctuated::Punctuated, spanned::Spanned, token::And, Attribute, Error, Expr, + ExprLit, FnArg, GenericArgument, Ident, ImplItem, ItemImpl, Lit, Meta, MetaNameValue, Pat, + Path, PathArguments, Result, ReturnType, Signature, Token, Type, TypePath, }; /// Generates the access to the `sc_client` crate. @@ -33,7 +34,7 @@ pub fn generate_crate_access() -> TokenStream { let renamed_name = Ident::new(&renamed_name, Span::call_site()); quote!(#renamed_name::__private) }, - Err(e) => + Err(e) => { if let Ok(FoundCrate::Name(name)) = crate_name(&"polkadot-sdk-frame").or_else(|_| crate_name(&"frame")) { @@ -47,7 +48,8 @@ pub fn generate_crate_access() -> TokenStream { } else { let err = Error::new(Span::call_site(), e).to_compile_error(); quote!( #err ) - }, + } + }, } } @@ -144,7 +146,7 @@ pub fn extract_parameter_names_types_and_borrows( return Err(Error::new(input.span(), "`self` parameter not supported!")), FnArg::Receiver(recv) => if recv.mutability.is_some() || recv.reference.is_none() { - return Err(Error::new(recv.span(), "Only `&self` is supported!")) + return Err(Error::new(recv.span(), "Only `&self` is supported!")); }, } } @@ -284,6 +286,85 @@ pub fn filter_cfg_attributes(attrs: &[syn::Attribute]) -> Vec { attrs.iter().filter(|a| a.path().is_ident("cfg")).cloned().collect() } +fn deprecation_msg_formatter(msg: &str) -> String { + format!( + r#"{msg} + help: the following are the possible correct uses +| +| #[deprecated = "reason"] +| +| #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] +| +| #[deprecated] +|"# + ) +} + +fn parse_deprecated_meta(crate_: &TokenStream, attr: &syn::Attribute) -> Result { + match &attr.meta { + Meta::List(meta_list) => { + let parsed = meta_list + .parse_args_with(Punctuated::::parse_terminated) + .map_err(|e| Error::new(attr.span(), e.to_string()))?; + let (note, since) = parsed.iter().try_fold((None, None), |mut acc, item| { + let value = match &item.value { + Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }) => Ok(lit), + _ => Err(Error::new( + attr.span(), + deprecation_msg_formatter( + "Invalid deprecation attribute: expected string literal", + ), + )), + }?; + if item.path.is_ident("note") { + acc.0.replace(value); + } else if item.path.is_ident("since") { + acc.1.replace(value); + } + Ok::<(Option<&syn::Lit>, Option<&syn::Lit>), Error>(acc) + })?; + note.map_or_else( + || Err(Error::new(attr.span(), deprecation_msg_formatter( + "Invalid deprecation attribute: missing `note`"))), + |note| { + let since = if let Some(str) = since { + quote! { Some(#str) } + } else { + quote! { None } + }; + let doc = quote! { #crate_::metadata_ir::DeprecationStatusIR::Deprecated { note: #note, since: #since }}; + Ok(doc) + }, + ) + }, + Meta::NameValue(MetaNameValue { + value: Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }), + .. + }) => { + // #[deprecated = "lit"] + let doc = quote! { #crate_::metadata_ir::DeprecationStatusIR::Deprecated { note: #lit, since: None } }; + Ok(doc) + }, + Meta::Path(_) => { + // #[deprecated] + Ok(quote! { #crate_::metadata_ir::DeprecationStatusIR::DeprecatedWithoutNote }) + }, + _ => Err(Error::new( + attr.span(), + deprecation_msg_formatter("Invalid deprecation attribute: expected string literal"), + )), + } +} + +/// collects deprecation attribute if its present. +pub fn get_deprecation(crate_: &TokenStream, attrs: &[syn::Attribute]) -> Result { + attrs + .iter() + .find(|a| a.path().is_ident("deprecated")) + .map(|a| parse_deprecated_meta(&crate_, a)) + .unwrap_or_else(|| Ok(quote! {#crate_::metadata_ir::DeprecationStatusIR::NotDeprecated})) +} + #[cfg(test)] mod tests { use assert_matches::assert_matches; @@ -330,4 +411,38 @@ mod tests { assert_eq!(cfg_std, filtered[0]); assert_eq!(cfg_benchmarks, filtered[1]); } + + #[test] + fn check_deprecated_attr() { + const FIRST: &'static str = "hello"; + const SECOND: &'static str = "WORLD"; + + let simple: Attribute = parse_quote!(#[deprecated]); + let simple_path: Attribute = parse_quote!(#[deprecated = #FIRST]); + let meta_list: Attribute = parse_quote!(#[deprecated(note = #FIRST)]); + let meta_list_with_since: Attribute = + parse_quote!(#[deprecated(note = #FIRST, since = #SECOND)]); + let extra_fields: Attribute = + parse_quote!(#[deprecated(note = #FIRST, since = #SECOND, extra = "Test")]); + assert_eq!( + get_deprecation("e! { crate }, &[simple]).unwrap().to_string(), + quote! { crate::metadata_ir::DeprecationStatusIR::DeprecatedWithoutNote }.to_string() + ); + assert_eq!( + get_deprecation("e! { crate }, &[simple_path]).unwrap().to_string(), + quote! { crate::metadata_ir::DeprecationStatusIR::Deprecated { note: #FIRST, since: None } }.to_string() + ); + assert_eq!( + get_deprecation("e! { crate }, &[meta_list]).unwrap().to_string(), + quote! { crate::metadata_ir::DeprecationStatusIR::Deprecated { note: #FIRST, since: None } }.to_string() + ); + assert_eq!( + get_deprecation("e! { crate }, &[meta_list_with_since]).unwrap().to_string(), + quote! { crate::metadata_ir::DeprecationStatusIR::Deprecated { note: #FIRST, since: Some(#SECOND) }}.to_string() + ); + assert_eq!( + get_deprecation("e! { crate }, &[extra_fields]).unwrap().to_string(), + quote! { crate::metadata_ir::DeprecationStatusIR::Deprecated { note: #FIRST, since: Some(#SECOND) }}.to_string() + ); + } } diff --git a/substrate/primitives/api/src/lib.rs b/substrate/primitives/api/src/lib.rs index 4b5c35562bde..700e212688c8 100644 --- a/substrate/primitives/api/src/lib.rs +++ b/substrate/primitives/api/src/lib.rs @@ -258,6 +258,11 @@ pub const MAX_EXTRINSIC_DEPTH: u32 = 256; /// ``` /// Note that the latest version (4 in our example above) always contains all methods from all /// the versions before. +/// +/// ## Note on deprecation. +/// +/// - Usage of `deprecated` attribute will propagate deprecation information to the metadata. +/// - For general usage examples of `deprecated` attribute please refer to pub use sp_api_proc_macro::decl_runtime_apis; /// Tags given trait implementations as runtime apis. diff --git a/substrate/primitives/api/test/tests/ui/deprecation_info.rs b/substrate/primitives/api/test/tests/ui/deprecation_info.rs new file mode 100644 index 000000000000..61f93fcd921d --- /dev/null +++ b/substrate/primitives/api/test/tests/ui/deprecation_info.rs @@ -0,0 +1,29 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +sp_api::decl_runtime_apis! { + pub trait Api { + #[deprecated(unknown_kw = "test")] + fn test(); + #[deprecated(since = 5)] + fn test2(); + #[deprecated = 5] + fn test3(); + } +} + +fn main() {} diff --git a/substrate/primitives/api/test/tests/ui/deprecation_info.stderr b/substrate/primitives/api/test/tests/ui/deprecation_info.stderr new file mode 100644 index 000000000000..2466c3ea5d50 --- /dev/null +++ b/substrate/primitives/api/test/tests/ui/deprecation_info.stderr @@ -0,0 +1,25 @@ +error: Invalid deprecation attribute: missing `note` + help: the following are the possible correct uses + | + | #[deprecated = "reason"] + | + | #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] + | + | #[deprecated] + | + --> tests/ui/deprecation_info.rs:20:3 + | +20 | #[deprecated(unknown_kw = "test")] + | ^ + +error[E0541]: unknown meta item 'unknown_kw' + --> tests/ui/deprecation_info.rs:20:16 + | +20 | #[deprecated(unknown_kw = "test")] + | ^^^^^^^^^^^^^^^^^^^ expected one of `since`, `note` + +error[E0565]: literal in `deprecated` value must be a string + --> tests/ui/deprecation_info.rs:22:24 + | +22 | #[deprecated(since = 5)] + | ^ diff --git a/substrate/primitives/metadata-ir/src/types.rs b/substrate/primitives/metadata-ir/src/types.rs index b05f26ff55d4..4ebe8c25a675 100644 --- a/substrate/primitives/metadata-ir/src/types.rs +++ b/substrate/primitives/metadata-ir/src/types.rs @@ -15,11 +15,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use codec::Encode; +use codec::{Compact, Encode}; use scale_info::{ form::{Form, MetaForm, PortableForm}, - prelude::vec::Vec, - IntoPortable, MetaType, Registry, + prelude::{collections::BTreeMap, vec::Vec}, + IntoPortable, Registry, }; /// The intermediate representation for the runtime metadata. @@ -52,6 +52,8 @@ pub struct RuntimeApiMetadataIR { pub methods: Vec>, /// Trait documentation. pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatusIR, } impl IntoPortable for RuntimeApiMetadataIR { @@ -62,6 +64,7 @@ impl IntoPortable for RuntimeApiMetadataIR { name: self.name.into_portable(registry), methods: registry.map_into_portable(self.methods), docs: registry.map_into_portable(self.docs), + deprecation_info: self.deprecation_info.into_portable(registry), } } } @@ -77,6 +80,8 @@ pub struct RuntimeApiMethodMetadataIR { pub output: T::Type, /// Method documentation. pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatusIR, } impl IntoPortable for RuntimeApiMethodMetadataIR { @@ -88,6 +93,7 @@ impl IntoPortable for RuntimeApiMethodMetadataIR { inputs: registry.map_into_portable(self.inputs), output: registry.register_type(&self.output), docs: registry.map_into_portable(self.docs), + deprecation_info: self.deprecation_info.into_portable(registry), } } } @@ -132,6 +138,8 @@ pub struct PalletMetadataIR { pub index: u8, /// Pallet documentation. pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatusIR, } impl IntoPortable for PalletMetadataIR { @@ -147,6 +155,7 @@ impl IntoPortable for PalletMetadataIR { error: self.error.map(|error| error.into_portable(registry)), index: self.index, docs: registry.map_into_portable(self.docs), + deprecation_info: self.deprecation_info.into_portable(registry), } } } @@ -245,6 +254,8 @@ pub struct StorageEntryMetadataIR { pub default: Vec, /// Storage entry documentation. pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatusIR, } impl IntoPortable for StorageEntryMetadataIR { @@ -257,6 +268,7 @@ impl IntoPortable for StorageEntryMetadataIR { ty: self.ty.into_portable(registry), default: self.default, docs: registry.map_into_portable(self.docs), + deprecation_info: self.deprecation_info.into_portable(registry), } } } @@ -331,19 +343,18 @@ impl IntoPortable for StorageEntryTypeIR { pub struct PalletCallMetadataIR { /// The corresponding enum type for the pallet call. pub ty: T::Type, + /// Deprecation status of the pallet call + pub deprecation_info: DeprecationInfoIR, } impl IntoPortable for PalletCallMetadataIR { type Output = PalletCallMetadataIR; fn into_portable(self, registry: &mut Registry) -> Self::Output { - PalletCallMetadataIR { ty: registry.register_type(&self.ty) } - } -} - -impl From for PalletCallMetadataIR { - fn from(ty: MetaType) -> Self { - Self { ty } + PalletCallMetadataIR { + ty: registry.register_type(&self.ty), + deprecation_info: self.deprecation_info.into_portable(registry), + } } } @@ -352,19 +363,18 @@ impl From for PalletCallMetadataIR { pub struct PalletEventMetadataIR { /// The Event type. pub ty: T::Type, + /// Deprecation info of the event + pub deprecation_info: DeprecationInfoIR, } impl IntoPortable for PalletEventMetadataIR { type Output = PalletEventMetadataIR; fn into_portable(self, registry: &mut Registry) -> Self::Output { - PalletEventMetadataIR { ty: registry.register_type(&self.ty) } - } -} - -impl From for PalletEventMetadataIR { - fn from(ty: MetaType) -> Self { - Self { ty } + PalletEventMetadataIR { + ty: registry.register_type(&self.ty), + deprecation_info: self.deprecation_info.into_portable(registry), + } } } @@ -379,6 +389,8 @@ pub struct PalletConstantMetadataIR { pub value: Vec, /// Documentation of the constant. pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatusIR, } impl IntoPortable for PalletConstantMetadataIR { @@ -390,6 +402,7 @@ impl IntoPortable for PalletConstantMetadataIR { ty: registry.register_type(&self.ty), value: self.value, docs: registry.map_into_portable(self.docs), + deprecation_info: self.deprecation_info.into_portable(registry), } } } @@ -399,19 +412,18 @@ impl IntoPortable for PalletConstantMetadataIR { pub struct PalletErrorMetadataIR { /// The error type information. pub ty: T::Type, + /// Deprecation info + pub deprecation_info: DeprecationInfoIR, } impl IntoPortable for PalletErrorMetadataIR { type Output = PalletErrorMetadataIR; fn into_portable(self, registry: &mut Registry) -> Self::Output { - PalletErrorMetadataIR { ty: registry.register_type(&self.ty) } - } -} - -impl From for PalletErrorMetadataIR { - fn from(ty: MetaType) -> Self { - Self { ty } + PalletErrorMetadataIR { + ty: registry.register_type(&self.ty), + deprecation_info: self.deprecation_info.into_portable(registry), + } } } @@ -451,3 +463,61 @@ impl IntoPortable for OuterEnumsIR { } } } + +/// Deprecation status for an entry inside MetadataIR +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub enum DeprecationStatusIR { + /// Entry is not deprecated + NotDeprecated, + /// Deprecated without a note. + DeprecatedWithoutNote, + /// Entry is deprecated with an note and an optional `since` field. + Deprecated { + /// Note explaining the deprecation + note: T::String, + /// Optional value for denoting version when the deprecation occured + since: Option, + }, +} +impl IntoPortable for DeprecationStatusIR { + type Output = DeprecationStatusIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + match self { + Self::Deprecated { note, since } => { + let note = note.into_portable(registry); + let since = since.map(|x| x.into_portable(registry)); + DeprecationStatusIR::Deprecated { note, since } + }, + Self::DeprecatedWithoutNote => DeprecationStatusIR::DeprecatedWithoutNote, + Self::NotDeprecated => DeprecationStatusIR::NotDeprecated, + } + } +} +/// Deprecation info for an enums/errors/calls. +/// Denotes full/partial deprecation of the type +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub enum DeprecationInfoIR { + /// Type is not deprecated + NotDeprecated, + /// Entry is fully deprecated. + ItemDeprecated(DeprecationStatusIR), + /// Entry is partially deprecated. + VariantsDeprecated(BTreeMap, DeprecationStatusIR>), +} +impl IntoPortable for DeprecationInfoIR { + type Output = DeprecationInfoIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + match self { + Self::VariantsDeprecated(entries) => { + let entries = + entries.into_iter().map(|(k, entry)| (k, entry.into_portable(registry))); + DeprecationInfoIR::VariantsDeprecated(entries.collect()) + }, + Self::ItemDeprecated(deprecation) => + DeprecationInfoIR::ItemDeprecated(deprecation.into_portable(registry)), + Self::NotDeprecated => DeprecationInfoIR::NotDeprecated, + } + } +}