From ea5fb02e9ae819771c05e1792de30b1352175ea3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Wed, 11 Sep 2024 13:00:34 +0200 Subject: [PATCH 1/7] Send balance when contract doesn't exist (#5664) Fixes #5577 I decided to bubble up the error from where we actually try to load the contract info. This helps to make sure that we don't miss some entry point by accident. The draw back is that we have to live with some additional `.expect`. @pgherveou With this logic the proxy and its runtime part should be completely unaware whether something is a contract call or a balance transfer. They should just route everything into pallet_revive. --------- Co-authored-by: Cyrill Leutwiler --- prdoc/pr_5664.prdoc | 11 + .../fixtures/contracts/call_return_code.rs | 3 +- substrate/frame/revive/src/exec.rs | 275 ++++++++++++------ substrate/frame/revive/src/primitives.rs | 2 +- substrate/frame/revive/src/tests.rs | 79 +++-- substrate/frame/revive/src/wasm/runtime.rs | 3 +- substrate/frame/revive/uapi/src/flags.rs | 1 + 7 files changed, 256 insertions(+), 118 deletions(-) create mode 100644 prdoc/pr_5664.prdoc 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/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; From 47cc5995cb93d79cb0f23df63b13b85e97327807 Mon Sep 17 00:00:00 2001 From: Maksym H <1177472+mordamax@users.noreply.github.com> Date: Wed, 11 Sep 2024 12:57:31 +0100 Subject: [PATCH 2/7] fix cmd detached commit (#5672) return the repo/ref as .sha creates detached branch and doesn't let to push back the changes --- .github/workflows/cmd.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cmd.yml b/.github/workflows/cmd.yml index 645b5444aa0f..5498beb50ccb 100644 --- a/.github/workflows/cmd.yml +++ b/.github/workflows/cmd.yml @@ -146,8 +146,6 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - name: Get command uses: actions-ecosystem/action-regex-match@v2 @@ -333,7 +331,8 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: - ref: ${{ github.event.pull_request.head.sha }} + repository: ${{ needs.get-pr-branch.outputs.repo }} + ref: ${{ needs.get-pr-branch.outputs.pr-branch }} - name: Install dependencies for bench if: startsWith(steps.get-pr-comment.outputs.group2, 'bench') @@ -360,10 +359,12 @@ jobs: git config --local user.email "action@github.com" git config --local user.name "GitHub Action" - git pull --rebase origin ${{ needs.get-pr-branch.outputs.pr-branch }} git add . git restore --staged Cargo.lock # ignore changes in Cargo.lock git commit -m "Update from ${{ github.actor }} running command '${{ steps.get-pr-comment.outputs.group2 }}'" || true + + git pull --rebase origin ${{ needs.get-pr-branch.outputs.pr-branch }} + git push origin ${{ needs.get-pr-branch.outputs.pr-branch }} else echo "Nothing to commit"; From 7d30806f20944218d3fda130f0ad513cd2948fca Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Wed, 11 Sep 2024 15:46:18 +0200 Subject: [PATCH 3/7] [pallet-contracts] Remove riscv support (#5665) --- Cargo.lock | 2 - prdoc/pr_5665.prdoc | 17 + substrate/frame/contracts/fixtures/Cargo.toml | 4 - substrate/frame/contracts/fixtures/build.rs | 64 +--- .../fixtures/contracts/common/src/lib.rs | 7 - substrate/frame/contracts/fixtures/src/lib.rs | 2 - substrate/frame/contracts/uapi/Cargo.toml | 3 - substrate/frame/contracts/uapi/src/host.rs | 5 +- .../frame/contracts/uapi/src/host/riscv32.rs | 327 ------------------ substrate/frame/contracts/uapi/src/lib.rs | 4 +- 10 files changed, 21 insertions(+), 414 deletions(-) create mode 100644 prdoc/pr_5665.prdoc delete mode 100644 substrate/frame/contracts/uapi/src/host/riscv32.rs diff --git a/Cargo.lock b/Cargo.lock index 8abecaf5e9ba..35dc61928178 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10363,7 +10363,6 @@ dependencies = [ "anyhow", "frame-system", "parity-wasm", - "polkavm-linker 0.9.2", "sp-runtime", "tempfile", "toml 0.8.12", @@ -10423,7 +10422,6 @@ dependencies = [ "bitflags 1.3.2", "parity-scale-codec", "paste", - "polkavm-derive 0.9.1", "scale-info", ] 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/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 { From 0f7acb52ada19ef934189a2ff02bdb5981634ead Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 11 Sep 2024 16:08:00 +0200 Subject: [PATCH 4/7] [CI] Fix parity-publish (#5670) Install with `--locked` to try to fix the CI. Signed-off-by: Oliver Tale-Yazdi --- .github/workflows/check-semver.yml | 2 +- .github/workflows/publish-check-crates.yml | 2 +- .github/workflows/publish-claim-crates.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check-semver.yml b/.github/workflows/check-semver.yml index 2c06df5a5092..b5866e0ce414 100644 --- a/.github/workflows/check-semver.yml +++ b/.github/workflows/check-semver.yml @@ -73,7 +73,7 @@ jobs: - name: install parity-publish # Set the target dir to cache the build. - run: CARGO_TARGET_DIR=./target/ cargo install parity-publish@0.8.0 -q + run: CARGO_TARGET_DIR=./target/ cargo install parity-publish@0.8.0 --locked -q - name: check semver run: | diff --git a/.github/workflows/publish-check-crates.yml b/.github/workflows/publish-check-crates.yml index cffa9a230c47..a5af04118572 100644 --- a/.github/workflows/publish-check-crates.yml +++ b/.github/workflows/publish-check-crates.yml @@ -24,7 +24,7 @@ jobs: cache-on-failure: true - name: install parity-publish - run: cargo install parity-publish@0.8.0 + run: cargo install parity-publish@0.8.0 --locked -q - name: parity-publish check run: parity-publish --color always check --allow-unpublished diff --git a/.github/workflows/publish-claim-crates.yml b/.github/workflows/publish-claim-crates.yml index 77f04861bfff..f9bc6ce4daea 100644 --- a/.github/workflows/publish-claim-crates.yml +++ b/.github/workflows/publish-claim-crates.yml @@ -18,7 +18,7 @@ jobs: cache-on-failure: true - name: install parity-publish - run: cargo install parity-publish@0.8.0 + run: cargo install parity-publish@0.8.0 --locked -q - name: parity-publish claim env: From 4e7c9e7fc3024d6cfd706bbfd14f3307985f3ae3 Mon Sep 17 00:00:00 2001 From: Niklas Adolfsson Date: Wed, 11 Sep 2024 18:35:21 +0200 Subject: [PATCH 5/7] rpc server: fix deny unsafe on RpcMethods::Auto (#5678) 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 --------- Co-authored-by: ggwpez --- prdoc/pr_5678.prdoc | 14 ++++++++++++++ substrate/client/rpc-servers/src/utils.rs | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 prdoc/pr_5678.prdoc 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, } } From 8f6699beadc220503721db3e60d0cfaa21f81b64 Mon Sep 17 00:00:00 2001 From: Ankan <10196091+Ank4n@users.noreply.github.com> Date: Wed, 11 Sep 2024 20:12:44 +0200 Subject: [PATCH 6/7] [Staking] Propagate actual system error while staking::bond (#5627) Trivial and self explanatory changes. Wrapping err such as these makes debugging harder as well as does not really give any meaningful reason why this happened. The increment of consumer can genuinely fail if there are no providers (account does not exist) or it reaches max consumers. In both cases, its better to propagate the actual System err. --- substrate/frame/staking/src/pallet/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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); From ec9a734f9a1f9aad839f0fd1d549179d7b1354a4 Mon Sep 17 00:00:00 2001 From: Pavlo Khrystenko <45178695+pkhry@users.noreply.github.com> Date: Wed, 11 Sep 2024 23:56:52 +0200 Subject: [PATCH 7/7] Deprecation info support in RuntimeMetadataIR (#4851) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description: * Adds `DeprecationStatusIR` enum to sp_metadata_ir. Deprecation info for simple items. * Adds `DeprecationInfoIR` enum to sp_metadata_ir. It is a deprecation info for an enums/errors/calls. Contains `DeprecationStatusIR`. Denotes full/partial deprecation of the type or its variants/calls * Adds `deprecation_info` field to - `RuntimeApiMetadataIR` - `RuntimeApiMethodMetadataIR` - `StorageEntryMetadataIR` - `PalletConstantMetadataIR` - `PalletCallMetadataIR` - `PalletMetadataIR` - `PalletEventMetadataIR` - `PalletErrorMetadataIR` ### Testing done: - Unit tests to check whether or not correct `note`/`since` texts are getting propagated to the metadata structs. - UI test to check for error message in case of incorrect attribute usage. There's also some test updates to make sure that deprecation attributes are getting propagated to the relevant structs. see: #4098, Solution: A ### Examples of produced deprecation info metadata They can be found in: - Tests for `frame-support` - hackmd link https://hackmd.io/@Zett98/Bys0YgbcR --------- Co-authored-by: GitHub Action Co-authored-by: command-bot <> Co-authored-by: Bastian Köcher --- prdoc/pr_4851.prdoc | 40 ++++ .../examples/proc_main/inject_runtime_type.rs | 1 + .../procedural/examples/proc_main/runtime.rs | 2 + .../src/construct_runtime/expand/metadata.rs | 17 +- .../procedural/src/construct_runtime/parse.rs | 35 ++-- .../support/procedural/src/deprecation.rs | 181 ++++++++++++++++++ substrate/frame/support/procedural/src/lib.rs | 5 +- .../procedural/src/pallet/expand/call.rs | 14 +- .../procedural/src/pallet/expand/constants.rs | 44 +++-- .../procedural/src/pallet/expand/error.rs | 24 +++ .../procedural/src/pallet/expand/event.rs | 25 ++- .../src/pallet/expand/pallet_struct.rs | 19 +- .../procedural/src/pallet/expand/storage.rs | 29 +-- .../procedural/src/pallet/parse/call.rs | 48 ++--- .../procedural/src/pallet/parse/config.rs | 40 ++-- .../procedural/src/pallet/parse/error.rs | 4 +- .../procedural/src/pallet/parse/event.rs | 15 +- .../src/pallet/parse/extra_constants.rs | 19 +- .../procedural/src/pallet/parse/mod.rs | 15 +- .../procedural/src/pallet/parse/storage.rs | 33 ++-- .../procedural/src/runtime/parse/mod.rs | 12 +- .../procedural/src/runtime/parse/pallet.rs | 3 +- substrate/frame/support/src/lib.rs | 42 ++++ .../support/src/storage/generator/map.rs | 6 +- .../support/src/storage/types/counted_map.rs | 13 +- .../support/src/storage/types/counted_nmap.rs | 57 +++++- .../support/src/storage/types/double_map.rs | 21 +- .../frame/support/src/storage/types/map.rs | 21 +- .../frame/support/src/storage/types/mod.rs | 6 +- .../frame/support/src/storage/types/nmap.rs | 49 ++++- .../frame/support/src/storage/types/value.rs | 21 +- substrate/frame/support/src/tests/mod.rs | 52 ++++- .../undefined_event_part.stderr | 20 ++ .../support/test/tests/enum_deprecation.rs | 173 +++++++++++++++++ .../frame/support/test/tests/instance.rs | 3 + substrate/frame/support/test/tests/pallet.rs | 62 +++++- .../support/test/tests/runtime_metadata.rs | 31 ++- .../api/proc-macro/src/impl_runtime_apis.rs | 8 +- .../api/proc-macro/src/runtime_metadata.rs | 14 +- .../primitives/api/proc-macro/src/utils.rs | 125 +++++++++++- substrate/primitives/api/src/lib.rs | 5 + .../api/test/tests/ui/deprecation_info.rs | 29 +++ .../api/test/tests/ui/deprecation_info.stderr | 25 +++ substrate/primitives/metadata-ir/src/types.rs | 118 +++++++++--- 44 files changed, 1319 insertions(+), 207 deletions(-) create mode 100644 prdoc/pr_4851.prdoc create mode 100644 substrate/frame/support/procedural/src/deprecation.rs create mode 100644 substrate/frame/support/test/tests/enum_deprecation.rs create mode 100644 substrate/primitives/api/test/tests/ui/deprecation_info.rs create mode 100644 substrate/primitives/api/test/tests/ui/deprecation_info.stderr 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/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, + } + } +}