From c6a22afed16a46e1aad628779342598f0f2c5b7d Mon Sep 17 00:00:00 2001 From: Daan van der Plas <93204684+Daanvdplas@users.noreply.github.com> Date: Fri, 26 Jul 2024 11:00:18 +0200 Subject: [PATCH] feat: api fungibles pallet (#113) Co-authored-by: Frank Bell --- pallets/api/Cargo.toml | 46 ++-- primitives/src/lib.rs | 2 +- runtime/devnet/src/config/api.rs | 34 +++ runtime/devnet/src/extensions/mod.rs | 221 --------------- runtime/devnet/src/extensions/v0/assets.rs | 76 ------ runtime/devnet/src/extensions/v0/error.rs | 298 --------------------- runtime/devnet/src/extensions/v0/mod.rs | 2 - 7 files changed, 58 insertions(+), 621 deletions(-) create mode 100644 runtime/devnet/src/config/api.rs delete mode 100644 runtime/devnet/src/extensions/mod.rs delete mode 100644 runtime/devnet/src/extensions/v0/assets.rs delete mode 100644 runtime/devnet/src/extensions/v0/error.rs delete mode 100644 runtime/devnet/src/extensions/v0/mod.rs diff --git a/pallets/api/Cargo.toml b/pallets/api/Cargo.toml index d2d444bf..3b38ceb5 100644 --- a/pallets/api/Cargo.toml +++ b/pallets/api/Cargo.toml @@ -1,13 +1,13 @@ [package] -name = "pallet-api" authors.workspace = true description = "API pallet, enabling smart(er) contracts with the power of Polkadot" edition.workspace = true license.workspace = true +name = "pallet-api" version = "0.1.0" [package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] +targets = [ "x86_64-unknown-linux-gnu" ] [dependencies] codec.workspace = true @@ -31,30 +31,30 @@ sp-core.workspace = true sp-io.workspace = true [features] -default = ["std"] +default = [ "std" ] runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-assets/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", ] std = [ - "codec/std", - "frame-benchmarking/std", - "frame-support/std", - "frame-system/std", - "pallet-assets/std", - "pallet-balances/std", - "pop-chain-extension/std", - "scale-info/std", - "sp-core/std", - "sp-io/std", - "sp-runtime/std", - "sp-std/std", + "codec/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "pallet-assets/std", + "pallet-balances/std", + "pop-chain-extension/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", ] try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "sp-runtime/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "sp-runtime/try-runtime", ] diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index dbee0025..faaa3386 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -232,7 +232,7 @@ pub mod v0 { } /// Description of what went wrong when trying to complete an operation on a token. - #[derive(Encode, Decode, Clone, Debug, MaxEncodedLen, Eq, PartialEq, Ord, PartialOrd)] + #[derive(Encode, Decode, Debug, Eq, PartialEq)] #[cfg_attr(feature = "std", derive(TypeInfo))] pub enum TokenError { /// Funds are unavailable. diff --git a/runtime/devnet/src/config/api.rs b/runtime/devnet/src/config/api.rs new file mode 100644 index 00000000..ae179e4a --- /dev/null +++ b/runtime/devnet/src/config/api.rs @@ -0,0 +1,34 @@ +use crate::{config::assets::TrustBackedAssetsInstance, fungibles, Runtime, RuntimeCall}; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::traits::Contains; + +/// A query of runtime state. +#[derive(Encode, Decode, Debug, MaxEncodedLen)] +#[repr(u8)] +pub enum RuntimeRead { + /// Fungible token queries. + #[codec(index = 150)] + Fungibles(fungibles::Read), +} + +impl fungibles::Config for Runtime { + type AssetsInstance = TrustBackedAssetsInstance; + type WeightInfo = fungibles::weights::SubstrateWeight; +} + +/// A type to identify allowed calls to the Runtime from contracts. Used by Pop API +pub struct AllowedApiCalls; + +impl Contains for AllowedApiCalls { + fn contains(c: &RuntimeCall) -> bool { + use fungibles::Call as FungiblesCall; + matches!( + c, + RuntimeCall::Fungibles( + FungiblesCall::transfer { .. } + | FungiblesCall::approve { .. } + | FungiblesCall::increase_allowance { .. } + ) + ) + } +} diff --git a/runtime/devnet/src/extensions/mod.rs b/runtime/devnet/src/extensions/mod.rs deleted file mode 100644 index 8a6bf76e..00000000 --- a/runtime/devnet/src/extensions/mod.rs +++ /dev/null @@ -1,221 +0,0 @@ -use frame_support::{ - dispatch::{GetDispatchInfo, RawOrigin}, - pallet_prelude::*, - traits::{Contains, OriginTrait}, -}; -use pallet_contracts::{ - chain_extension::{ - BufInBufOutState, ChainExtension, ChargedAmount, Environment, Ext, InitState, RetVal, - }, - WeightInfo, -}; -use pop_primitives::storage_keys::RuntimeStateKeys; -use sp_core::crypto::UncheckedFrom; -use sp_runtime::{traits::Dispatchable, DispatchError}; -use sp_std::vec::Vec; - -use crate::{AccountId, AllowedApiCalls, RuntimeCall, RuntimeOrigin}; - -mod v0; - -const LOG_TARGET: &str = "pop-api::extension"; -// Versions: -const V0: u8 = 0; - -#[derive(Default)] -pub struct PopApiExtension; - -impl ChainExtension for PopApiExtension -where - T: pallet_contracts::Config - + frame_system::Config< - RuntimeOrigin = RuntimeOrigin, - AccountId = AccountId, - RuntimeCall = RuntimeCall, - >, - T::AccountId: UncheckedFrom + AsRef<[u8]>, -{ - fn call(&mut self, env: Environment) -> Result - where - E: Ext, - { - log::debug!(target:LOG_TARGET, " extension called "); - match v0::FuncId::try_from(env.func_id())? { - v0::FuncId::Dispatch => { - match dispatch::(env) { - Ok(()) => Ok(RetVal::Converging(0)), - Err(DispatchError::Module(error)) => { - // encode status code = pallet index in runtime + error index, allowing for - // 999 errors - Ok(RetVal::Converging( - (error.index as u32 * 1_000) + u32::from_le_bytes(error.error), - )) - }, - Err(e) => Err(e), - } - }, - v0::FuncId::ReadState => { - read_state::(env)?; - Ok(RetVal::Converging(0)) - }, - } - } -} - -pub mod v0 { - #[derive(Debug)] - pub enum FuncId { - Dispatch, - ReadState, - } - u32::from_le_bytes(encoded_error) -} - -/// Enum representing the different function identifiers used in the Pop API chain extension. -/// -/// The `FuncId` enum specifies the available functions that can be called through the Pop API chain -/// extension. Each variant corresponds to a specific functionality provided by the chain extension, -/// facilitating the interaction between smart contracts and the runtime. -/// -/// - `Dispatch`: Represents a function call to dispatch a runtime call. -/// - `ReadState`: Represents a function call to read the state from the runtime. -/// - `SendXcm`: Represents a function call to send an XCM message. -#[derive(Debug)] -pub enum FuncId { - Dispatch, - ReadState, - SendXcm, -} - -impl TryFrom for FuncId { - type Error = DispatchError; - - /// Attempts to convert a `u8` value to its corresponding `FuncId` variant. - /// - /// If the `u8` value does not match any known function identifier, it returns a - /// `DispatchError::Other` indicating an unknown function ID. - fn try_from(func_id: u8) -> Result { - let id = match func_id { - 0x0 => Self::Dispatch, - 0x1 => Self::ReadState, - _ => { - log::error!("called an unregistered `func_id`: {:}", func_id); - // TODO: Other error. - return Err(DispatchError::Other("unimplemented func_id")); - }, - }; - Ok(id) - } -} - -fn dispatch_call( - env: &mut Environment, - call: RuntimeCall, - mut origin: RuntimeOrigin, - log_prefix: &str, -) -> Result<(), DispatchError> -where - E: Ext, -{ - let charged_dispatch_weight = env.charge_weight(call.get_dispatch_info().weight)?; - - log::debug!(target:LOG_TARGET, "{} inputted RuntimeCall: {:?}", log_prefix, call); - - origin.add_filter(AllowedPopApiCalls::contains); - - match call.dispatch(origin) { - Ok(info) => { - log::debug!(target:LOG_TARGET, "{} success, actual weight: {:?}", log_prefix, info.actual_weight); - - // refund weight if the actual weight is less than the charged weight - if let Some(actual_weight) = info.actual_weight { - env.adjust_weight(charged_dispatch_weight, actual_weight); - } - - Ok(()) - }, - Err(err) => { - log::debug!(target:LOG_TARGET, "{} failed: error: {:?}", log_prefix, err.error); - Err(err.error) - }, - } -} - -fn charge_overhead_weight( - env: &mut Environment, - len: u32, - log_prefix: &str, -) -> Result -where - T: pallet_contracts::Config, - E: Ext, -{ - // calculate weight for reading bytes of `len` - // reference: https://github.com/paritytech/polkadot-sdk/pull/4233/files#:~:text=CopyToContract(len)%20%3D%3E%20T%3A%3AWeightInfo%3A%3Aseal_input(len)%2C - let base_weight: Weight = T::WeightInfo::seal_return(len); - - // debug_message weight is a good approximation of the additional overhead of going - // from contract layer to substrate layer. - // reference: https://github.com/paritytech/polkadot-sdk/pull/4233/files#:~:text=DebugMessage(len)%20%3D%3E%20T%3A%3AWeightInfo%3A%3Aseal_debug_message(len)%2C - let overhead = T::WeightInfo::seal_debug_message(len); - - let charged_weight = env.charge_weight(base_weight.saturating_add(overhead))?; - log::debug!(target: LOG_TARGET, "{} charged weight: {:?}", log_prefix, charged_weight); - - Ok(charged_weight) -} - -fn dispatch(env: Environment) -> Result<(), DispatchError> -where - T: pallet_contracts::Config, - RuntimeOrigin: From>, - E: Ext, -{ - const LOG_PREFIX: &str = " dispatch |"; - - let mut env = env.buf_in_buf_out(); - let len = env.in_len(); - - charge_overhead_weight::(&mut env, len, LOG_PREFIX)?; - - // read the input as RuntimeCall - let call: RuntimeCall = env.read_as_unbounded(len)?; - - // contract is the origin by default - let origin: RuntimeOrigin = RawOrigin::Signed(env.ext().address().clone()).into(); - - dispatch_call::(&mut env, call, origin, LOG_PREFIX) -} - -fn read_state(env: Environment) -> Result<(), DispatchError> -where - T: pallet_contracts::Config, - E: Ext, -{ - const LOG_PREFIX: &str = " read_state |"; - - let mut env = env.buf_in_buf_out(); - - // To be conservative, we charge the weight for reading the input bytes of a fixed-size type. - let base_weight: Weight = T::WeightInfo::seal_return(env.in_len()); - let charged_weight = env.charge_weight(base_weight)?; - - log::debug!(target:LOG_TARGET, "{} charged weight: {:?}", LOG_PREFIX, charged_weight); - - let key: RuntimeStateKeys = env.read_as()?; - - let result = match key { - _ => Vec::::default(), - } - .encode(); - - log::trace!( - target:LOG_TARGET, - "{} result: {:?}.", LOG_PREFIX, result - ); - env.write(&result, false, None).map_err(|e| { - log::trace!(target: LOG_TARGET, "{:?}", e); - // TODO: Other error. - DispatchError::Other("unable to write results to contract memory") - }) -} diff --git a/runtime/devnet/src/extensions/v0/assets.rs b/runtime/devnet/src/extensions/v0/assets.rs deleted file mode 100644 index c6b15b4e..00000000 --- a/runtime/devnet/src/extensions/v0/assets.rs +++ /dev/null @@ -1,76 +0,0 @@ -use crate::extensions::{ - AccountId as AccountId32, AssetId, - AssetsKeys::{self, *}, - Balance, Compact, Decode, DispatchError, MultiAddress, Runtime, TrustBackedAssetsInstance, -}; -use pop_primitives::AccountId; -use sp_std::vec::Vec; - -pub(crate) fn construct_assets_key( - call_index: u8, - params: Vec, -) -> Result { - match call_index { - 0 => { - let id = ::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(TotalSupply(id)) - }, - 1 => { - let (id, owner) = <(AssetId, AccountId)>::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(BalanceOf(id, owner)) - }, - 2 => { - let (id, owner, spender) = <(AssetId, AccountId, AccountId)>::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(Allowance(id, owner, spender)) - }, - 3 => { - let id = ::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(TokenName(id)) - }, - 4 => { - let id = ::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(TokenSymbol(id)) - }, - 5 => { - let id = ::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(TokenDecimals(id)) - }, - // other calls - _ => Err(DispatchError::Other("UnknownFunctionId")), - } -} - -pub(crate) fn construct_assets_call( - call_index: u8, - params: Vec, -) -> Result, DispatchError> { - match call_index { - 9 => { - let (id, target, amount) = <(AssetId, AccountId32, Balance)>::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(pallet_assets::Call::::transfer_keep_alive { - id: Compact(id), - target: MultiAddress::Id(target), - amount, - }) - }, - 22 => { - let (id, delegate, amount) = - <(AssetId, AccountId32, Balance)>::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(pallet_assets::Call::::approve_transfer { - id: Compact(id), - delegate: MultiAddress::Id(delegate), - amount, - }) - }, - // other calls - _ => Err(DispatchError::Other("UnknownFunctionId")), - } -} diff --git a/runtime/devnet/src/extensions/v0/error.rs b/runtime/devnet/src/extensions/v0/error.rs deleted file mode 100644 index b8af2250..00000000 --- a/runtime/devnet/src/extensions/v0/error.rs +++ /dev/null @@ -1,298 +0,0 @@ -#[cfg(test)] -use crate::extensions::convert_to_status_code; - -pub(crate) fn handle_unknown_error(encoded_error: &mut [u8; 4]) { - let unknown = match encoded_error[0] { - code if UNIT_ERRORS.contains(&code) => nested_errors(&encoded_error[1..], None), - // Single nested errors with a limit in their nesting. - // - // `TokenError`: has ten variants - translated to a limit of nine. - 7 => nested_errors(&encoded_error[1..], Some(9)), - // `ArithmeticError`: has 3 variants - translated to a limit of two. - 8 => nested_errors(&encoded_error[1..], Some(2)), - // `TransactionalError`: has 2 variants - translated to a limit of one. - 9 => nested_errors(&encoded_error[1..], Some(1)), - code if DOUBLE_NESTED_ERRORS.contains(&code) => nested_errors(&encoded_error[3..], None), - _ => true, - }; - if unknown { - encoded_error[..].rotate_right(1); - encoded_error[0] = 0u8; - } -} - -// Unit `Error` variants. -// (variant: index): -// - CannotLookup: 1, -// - BadOrigin: 2, -// - ConsumerRemaining: 4, -// - NoProviders: 5, -// - TooManyConsumers: 6, -// - Exhausted: 10, -// - Corruption: 11, -// - Unavailable: 12, -// - RootNotAllowed: 13, -// - UnknownFunctionId: 254, -// - DecodingFailed: 255, -const UNIT_ERRORS: [u8; 11] = [1, 2, 4, 5, 6, 10, 11, 12, 13, 254, 255]; - -#[cfg(test)] -const SINGLE_NESTED_ERRORS: [u8; 3] = [7, 8, 9]; - -// Double nested `Error` variants -// (variant: index): -// - Module: 3, -const DOUBLE_NESTED_ERRORS: [u8; 1] = [3]; - -// Checks for unknown nested errors within the `DispatchError`. -// - For single nested errors with a limit, it verifies if the nested value exceeds the limit. -// - For other nested errors, it checks if any subsequent bytes are non-zero. -// -// `nested_error` - The slice of bytes representing the nested error. -// `limit` - An optional limit for single nested errors. -fn nested_errors(nested_error: &[u8], limit: Option) -> bool { - match limit { - Some(l) => nested_error[0] > l || nested_error[1..].iter().any(|&x| x != 0u8), - None => nested_error.iter().any(|&x| x != 0u8), - } -} - -#[cfg(test)] -mod tests { - use super::*; - use pop_primitives::error::{ - ArithmeticError::*, - Error::{self, *}, - TokenError::*, - TransactionalError::*, - }; - use sp_runtime::DispatchError; - - // Compare all the different `DispatchError` variants with the expected `Error`. - #[test] - fn dispatch_error_to_error() { - let test_cases = vec![ - ( - DispatchError::Other(""), - (Other { dispatch_error_index: 0, error_index: 0, error: 0 }), - ), - (DispatchError::Other("UnknownFunctionId"), UnknownFunctionId), - (DispatchError::Other("DecodingFailed"), DecodingFailed), - (DispatchError::CannotLookup, CannotLookup), - (DispatchError::BadOrigin, BadOrigin), - ( - DispatchError::Module(sp_runtime::ModuleError { - index: 1, - error: [2, 0, 0, 0], - message: Some("hallo"), - }), - Module { index: 1, error: 2 }, - ), - (DispatchError::ConsumerRemaining, ConsumerRemaining), - (DispatchError::NoProviders, NoProviders), - (DispatchError::TooManyConsumers, TooManyConsumers), - (DispatchError::Token(sp_runtime::TokenError::BelowMinimum), Token(BelowMinimum)), - ( - DispatchError::Arithmetic(sp_runtime::ArithmeticError::Overflow), - Arithmetic(Overflow), - ), - ( - DispatchError::Transactional(sp_runtime::TransactionalError::LimitReached), - Transactional(LimitReached), - ), - (DispatchError::Exhausted, Exhausted), - (DispatchError::Corruption, Corruption), - (DispatchError::Unavailable, Unavailable), - (DispatchError::RootNotAllowed, RootNotAllowed), - ]; - for (dispatch_error, expected) in test_cases { - let status_code = crate::extensions::convert_to_status_code(dispatch_error, 0); - let error: Error = status_code.into(); - assert_eq!(error, expected); - } - } - - // Compare all the different `DispatchError::Other` possibilities with the expected `Error`. - #[test] - fn other_error() { - let test_cases = vec![ - ( - DispatchError::Other("Random"), - (Other { dispatch_error_index: 0, error_index: 0, error: 0 }), - ), - (DispatchError::Other("UnknownFunctionId"), UnknownFunctionId), - (DispatchError::Other("DecodingFailed"), DecodingFailed), - ]; - for (dispatch_error, expected) in test_cases { - let status_code = convert_to_status_code(dispatch_error, 0); - let error: Error = status_code.into(); - assert_eq!(error, expected); - } - } - - // Compare all the different `DispatchError::Module` nesting possibilities, which can not be - // handled, with the expected `Error`. See `double_nested_error_variants` fourth byte nesting. - #[test] - fn test_module_error() { - let test_cases = vec![ - DispatchError::Module(sp_runtime::ModuleError { - index: 1, - error: [2, 2, 0, 0], - message: Some("Random"), - }), - DispatchError::Module(sp_runtime::ModuleError { - index: 1, - error: [2, 2, 2, 0], - message: Some("Random"), - }), - DispatchError::Module(sp_runtime::ModuleError { - index: 1, - error: [2, 2, 2, 2], - message: Some("Random"), - }), - ]; - for dispatch_error in test_cases { - let status_code = convert_to_status_code(dispatch_error, 0); - let error: Error = status_code.into(); - assert_eq!(error, Other { dispatch_error_index: 3, error_index: 1, error: 2 }); - } - } - - // Converts 4 bytes into `Error` and handles unknown errors (used in `convert_to_status_code`). - fn into_error(mut error_bytes: [u8; 4]) -> Error { - handle_unknown_error(&mut error_bytes); - u32::from_le_bytes(error_bytes).into() - } - - // Tests the `handle_unknown_error` for `Error`, version 0. - // - // Unit variants: - // If the encoded value indicates a nested `Error` which is known by V0 as a - // unit variant, the encoded value is converted into `Error::Other`. - // - // Example: the error `BadOrigin` (encoded: `[2, 0, 0, 0]`) with a non-zero value for one - // of the bytes [1..4]: `[2, 0, 1, 0]` is converted into `[0, 2, 0, 1]` (shifting the bits - // one forward). This is decoded to `Error::Other { dispatch_error: 2, index: 0, error: 1 }`. - #[test] - fn unit_error_variants() { - let errors = vec![ - CannotLookup, - BadOrigin, - ConsumerRemaining, - NoProviders, - TooManyConsumers, - Exhausted, - Corruption, - Unavailable, - RootNotAllowed, - UnknownFunctionId, - DecodingFailed, - ]; - // Compare an `Error`, which is converted from an encoded value, with the expected `Error`. - for (i, &error_code) in UNIT_ERRORS.iter().enumerate() { - // No nesting and unit variant correctly returned. - assert_eq!(into_error([error_code, 0, 0, 0]), errors[i]); - // Unexpected second byte nested. - assert_eq!( - into_error([error_code, 1, 0, 0]), - (Other { dispatch_error_index: error_code, error_index: 1, error: 0 }), - ); - // Unexpected third byte nested. - assert_eq!( - into_error([error_code, 1, 1, 0]), - (Other { dispatch_error_index: error_code, error_index: 1, error: 1 }), - ); - // Unexpected fourth byte nested. - assert_eq!( - into_error([error_code, 1, 1, 1]), - (Other { dispatch_error_index: error_code, error_index: 1, error: 1 }), - ); - } - } - - // Single nested variants: - // If the encoded value indicates a double nested `Error` which is known by V0 - // as a single nested variant, the encoded value is converted into `Error::Other`. - // - // Example: the error `Arithmetic(Overflow)` (encoded: `[8, 1, 0, 0]`) with a non-zero - // value for one of the bytes [2..4]: `[8, 1, 1, 0]` is converted into `[0, 8, 1, 1]`. This is - // decoded to `Error::Other { dispatch_error: 8, index: 1, error: 1 }`. - #[test] - fn single_nested_error_variants() { - let errors = vec![ - [Token(FundsUnavailable), Token(OnlyProvider)], - [Arithmetic(Underflow), Arithmetic(Overflow)], - [Transactional(LimitReached), Transactional(NoLayer)], - ]; - // Compare an `Error`, which is converted from an encoded value, with the expected `Error`. - for (i, &error_code) in SINGLE_NESTED_ERRORS.iter().enumerate() { - // No nested and single nested variant correctly returned. - assert_eq!(into_error([error_code, 0, 0, 0]), errors[i][0]); - assert_eq!(into_error([error_code, 1, 0, 0]), errors[i][1]); - // Unexpected third byte nested. - assert_eq!( - into_error([error_code, 1, 1, 0]), - (Other { dispatch_error_index: error_code, error_index: 1, error: 1 }), - ); - // Unexpected fourth byte nested. - assert_eq!( - into_error([error_code, 1, 1, 1]), - Other { dispatch_error_index: error_code, error_index: 1, error: 1 }, - ); - } - } - - #[test] - fn single_nested_unknown_variants() { - // Unknown `TokenError` variant. - assert_eq!( - into_error([7, 10, 0, 0]), - Other { dispatch_error_index: 7, error_index: 10, error: 0 } - ); - // Unknown `Arithmetic` variant. - assert_eq!( - into_error([8, 3, 0, 0]), - Other { dispatch_error_index: 8, error_index: 3, error: 0 } - ); - // Unknown `Transactional` variant. - assert_eq!( - into_error([9, 2, 0, 0]), - Other { dispatch_error_index: 9, error_index: 2, error: 0 } - ); - } - - // Double nested variants: - // If the encoded value indicates a triple nested `Error` which is known by V0 - // as a double nested variant, the encoded value is converted into `Error::Other`. - // - // Example: the error `Module { index: 10, error 5 }` (encoded: `[3, 10, 5, 0]`) with a non-zero - // value for the last byte: `[3, 10, 5, 3]` is converted into `[0, 3, 10, 5]`. This is - // decoded to `Error::Other { dispatch_error: 3, index: 10, error: 5 }`. - #[test] - fn double_nested_error_variants() { - // Compare an `Error`, which is converted from an encoded value, with the expected `Error`. - // No nesting and unit variant correctly returned. - assert_eq!(into_error([3, 0, 0, 0]), Module { index: 0, error: 0 }); - // Allowed single nesting and variant correctly returned. - assert_eq!(into_error([3, 1, 0, 0]), Module { index: 1, error: 0 }); - // Allowed double nesting and variant correctly returned. - assert_eq!(into_error([3, 1, 1, 0]), Module { index: 1, error: 1 }); - // Unexpected fourth byte nested. - assert_eq!( - into_error([3, 1, 1, 1]), - Other { dispatch_error_index: 3, error_index: 1, error: 1 }, - ); - } - - #[test] - fn test_random_encoded_values() { - assert_eq!( - into_error([100, 100, 100, 100]), - Other { dispatch_error_index: 100, error_index: 100, error: 100 } - ); - assert_eq!( - into_error([200, 200, 200, 200]), - Other { dispatch_error_index: 200, error_index: 200, error: 200 } - ); - } -} diff --git a/runtime/devnet/src/extensions/v0/mod.rs b/runtime/devnet/src/extensions/v0/mod.rs deleted file mode 100644 index 6406e08f..00000000 --- a/runtime/devnet/src/extensions/v0/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub(crate) mod assets; -pub(crate) mod error;