From 5b7dd3d3576cf986900b18cef2811da6388e48be Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Wed, 27 Dec 2023 20:11:48 +0100 Subject: [PATCH] View-Only chain extension for `pallet-uniques` (#5) * add chain extension * add extension to local-runtime * add extension to shibuya-runtime * fix weight * fix --- Cargo.lock | 31 +++ Cargo.toml | 3 + chain-extensions/pallet-uniques/Cargo.toml | 41 ++++ chain-extensions/pallet-uniques/src/lib.rs | 211 ++++++++++++++++++ .../pallet-uniques/src/weights.rs | 77 +++++++ chain-extensions/types/uniques/Cargo.toml | 25 +++ chain-extensions/types/uniques/src/lib.rs | 37 +++ runtime/local/Cargo.toml | 2 + runtime/local/src/chain_extensions.rs | 7 + runtime/local/src/lib.rs | 1 + runtime/shibuya/Cargo.toml | 2 + runtime/shibuya/src/chain_extensions.rs | 7 + runtime/shibuya/src/lib.rs | 1 + 13 files changed, 445 insertions(+) create mode 100644 chain-extensions/pallet-uniques/Cargo.toml create mode 100644 chain-extensions/pallet-uniques/src/lib.rs create mode 100644 chain-extensions/pallet-uniques/src/weights.rs create mode 100644 chain-extensions/types/uniques/Cargo.toml create mode 100644 chain-extensions/types/uniques/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 3bed5d4f35..113c9ebfbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6030,6 +6030,7 @@ dependencies = [ "pallet-block-rewards-hybrid", "pallet-chain-extension-assets", "pallet-chain-extension-unified-accounts", + "pallet-chain-extension-uniques", "pallet-chain-extension-xvm", "pallet-collective", "pallet-contracts", @@ -7506,6 +7507,25 @@ dependencies = [ "unified-accounts-chain-extension-types", ] +[[package]] +name = "pallet-chain-extension-uniques" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "log", + "num-traits", + "pallet-contracts", + "pallet-contracts-primitives", + "pallet-uniques", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", + "uniques-chain-extension-types", +] + [[package]] name = "pallet-chain-extension-xvm" version = "0.1.1" @@ -13188,6 +13208,7 @@ dependencies = [ "pallet-balances", "pallet-chain-extension-assets", "pallet-chain-extension-unified-accounts", + "pallet-chain-extension-uniques", "pallet-chain-extension-xvm", "pallet-collator-selection", "pallet-collective", @@ -15397,6 +15418,16 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "uniques-chain-extension-types" +version = "0.1.0" +dependencies = [ + "frame-system", + "pallet-contracts", + "parity-scale-codec", + "scale-info", +] + [[package]] name = "universal-hash" version = "0.4.1" diff --git a/Cargo.toml b/Cargo.toml index 3e7f41a8c7..e6024cc266 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ members = [ "chain-extensions/pallet-assets", "chain-extensions/xvm", "chain-extensions/unified-accounts", + "chain-extensions/pallet-uniques", "chain-extensions/types/*", "vendor/evm-tracing", @@ -301,10 +302,12 @@ pallet-evm-precompile-unified-accounts = { path = "./precompiles/unified-account pallet-chain-extension-xvm = { path = "./chain-extensions/xvm", default-features = false } pallet-chain-extension-assets = { path = "./chain-extensions/pallet-assets", default-features = false } pallet-chain-extension-unified-accounts = { path = "./chain-extensions/unified-accounts", default-features = false } +pallet-chain-extension-uniques = { path = "./chain-extensions/pallet-uniques", default-features = false } xvm-chain-extension-types = { path = "./chain-extensions/types/xvm", default-features = false } assets-chain-extension-types = { path = "./chain-extensions/types/assets", default-features = false } unified-accounts-chain-extension-types = { path = "./chain-extensions/types/unified-accounts", default-features = false } +uniques-chain-extension-types = { path = "./chain-extensions/types/uniques", default-features = false } precompile-utils = { path = "./precompiles/utils", default-features = false } diff --git a/chain-extensions/pallet-uniques/Cargo.toml b/chain-extensions/pallet-uniques/Cargo.toml new file mode 100644 index 0000000000..f989e56adc --- /dev/null +++ b/chain-extensions/pallet-uniques/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "pallet-chain-extension-uniques" +version = "0.1.0" +license = "Apache-2.0" +description = "Uniques chain extension for WASM contracts" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +uniques-chain-extension-types = { workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +log = { workspace = true } +num-traits = { workspace = true } +pallet-uniques = { workspace = true } +pallet-contracts = { workspace = true } +pallet-contracts-primitives = { workspace = true } +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "frame-support/std", + "frame-system/std", + "num-traits/std", + "pallet-contracts/std", + "pallet-contracts-primitives/std", + "scale-info/std", + "sp-std/std", + "sp-core/std", + "sp-runtime/std", + "pallet-uniques/std", + "uniques-chain-extension-types/std", +] diff --git a/chain-extensions/pallet-uniques/src/lib.rs b/chain-extensions/pallet-uniques/src/lib.rs new file mode 100644 index 0000000000..ebf285853b --- /dev/null +++ b/chain-extensions/pallet-uniques/src/lib.rs @@ -0,0 +1,211 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Astar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Astar. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod weights; + +use frame_support::traits::nonfungibles::{Inspect, InspectEnumerable}; +use pallet_contracts::chain_extension::{ + ChainExtension, Environment, Ext, InitState, RetVal, SysConfig, +}; +use parity_scale_codec::Encode; +use sp_runtime::traits::StaticLookup; +use sp_runtime::BoundedVec; +use sp_runtime::DispatchError; +use sp_std::marker::PhantomData; +use sp_std::vec::Vec; +use uniques_chain_extension_types::Outcome; + +type AccountIdLookup = <::Lookup as StaticLookup>::Source; + +enum UniquesFunc { + Owner, + CollectionOwner, + Attribute, + CollectionAttribute, + CanTransfer, + Collections, + Items, + Owned, + OwnedInCollection, +} + +impl TryFrom for UniquesFunc { + type Error = DispatchError; + + fn try_from(value: u16) -> Result { + match value { + 1 => Ok(UniquesFunc::Owner), + 2 => Ok(UniquesFunc::CollectionOwner), + 3 => Ok(UniquesFunc::Attribute), + 4 => Ok(UniquesFunc::CollectionAttribute), + 5 => Ok(UniquesFunc::CanTransfer), + 6 => Ok(UniquesFunc::Collections), + 7 => Ok(UniquesFunc::Items), + 8 => Ok(UniquesFunc::Owned), + 9 => Ok(UniquesFunc::OwnedInCollection), + _ => Err(DispatchError::Other( + "Unimplemented func_id for UniquesFunc", + )), + } + } +} + +/// Pallet Uniques chain extension. +pub struct UniquesExtension(PhantomData<(T, W)>); + +impl Default for UniquesExtension { + fn default() -> Self { + UniquesExtension(PhantomData) + } +} + +impl ChainExtension for UniquesExtension +where + T: pallet_uniques::Config + pallet_contracts::Config, + AccountIdLookup: From<::AccountId>, + ::AccountId: From<[u8; 32]>, + W: weights::WeightInfo, +{ + fn call(&mut self, env: Environment) -> Result + where + E: Ext, + { + let func_id = env.func_id().try_into()?; + let mut env = env.buf_in_buf_out(); + + match func_id { + UniquesFunc::Owner => { + let (collection_id, item): ( + ::CollectionId, + ::ItemId, + ) = env.read_as()?; + + let base_weight = ::owner(); + env.charge_weight(base_weight)?; + + let owner = pallet_uniques::Pallet::::owner(collection_id, item); + env.write(&owner.encode(), false, None)?; + } + UniquesFunc::CollectionOwner => { + let collection_id: ::CollectionId = env.read_as()?; + + let base_weight = ::collection_owner(); + env.charge_weight(base_weight)?; + + let owner = pallet_uniques::Pallet::::collection_owner(collection_id); + env.write(&owner.encode(), false, None)?; + } + UniquesFunc::Attribute => { + let (collection_id, item, key): ( + ::CollectionId, + ::ItemId, + BoundedVec::KeyLimit>, + ) = env.read_as()?; + + let base_weight = ::attribute(); + env.charge_weight(base_weight)?; + + let attribute = pallet_uniques::Pallet::::attribute(&collection_id, &item, &key); + env.write(&attribute.encode(), false, None)?; + } + UniquesFunc::CollectionAttribute => { + let (collection_id, key): ( + ::CollectionId, + BoundedVec::KeyLimit>, + ) = env.read_as()?; + + let base_weight = ::collection_attribute(); + env.charge_weight(base_weight)?; + + let attribute = + pallet_uniques::Pallet::::collection_attribute(&collection_id, &key); + env.write(&attribute.encode(), false, None)?; + } + UniquesFunc::CanTransfer => { + let (collection_id, item): ( + ::CollectionId, + ::ItemId, + ) = env.read_as()?; + + let base_weight = ::can_transfer(); + env.charge_weight(base_weight)?; + + let can_transfer = pallet_uniques::Pallet::::can_transfer(&collection_id, &item); + env.write(&can_transfer.encode(), false, None)?; + } + UniquesFunc::Collections => { + let read_bound: u32 = env.read_as()?; + + let base_weight = ::collections(read_bound); + env.charge_weight(base_weight)?; + + let collections: Vec<::CollectionId> = + pallet_uniques::Pallet::::collections().collect(); + + env.write(&collections.encode(), false, None)?; + } + UniquesFunc::Items => { + let (collection_id, read_bound): ( + ::CollectionId, + u32, + ) = env.read_as()?; + + let base_weight = ::items(read_bound); + env.charge_weight(base_weight)?; + + let items: Vec<::ItemId> = + pallet_uniques::Pallet::::items(&collection_id).collect(); + + env.write(&items.encode(), false, None)?; + } + UniquesFunc::Owned => { + let (who, read_bound): (T::AccountId, u32) = env.read_as()?; + + let items: Vec<( + ::CollectionId, + ::ItemId, + )> = pallet_uniques::Pallet::::owned(&who).collect(); + + let base_weight = ::owned(read_bound); + env.charge_weight(base_weight)?; + + env.write(&items.encode(), false, None)?; + } + UniquesFunc::OwnedInCollection => { + let (who, collection_id, read_bound): ( + T::AccountId, + ::CollectionId, + u32, + ) = env.read_as()?; + + let items: Vec<::ItemId> = + pallet_uniques::Pallet::::owned_in_collection(&collection_id, &who) + .collect(); + + let base_weight = ::owned_in_collection(read_bound); + env.charge_weight(base_weight)?; + + env.write(&items.encode(), false, None)?; + } + } + + Ok(RetVal::Converging(Outcome::Success as u32)) + } +} diff --git a/chain-extensions/pallet-uniques/src/weights.rs b/chain-extensions/pallet-uniques/src/weights.rs new file mode 100644 index 0000000000..fb847b3abb --- /dev/null +++ b/chain-extensions/pallet-uniques/src/weights.rs @@ -0,0 +1,77 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Astar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Astar. If not, see . + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet-uniques chain-extension. +pub trait WeightInfo { + fn owner() -> Weight; + fn collection_owner() -> Weight; + fn attribute() -> Weight; + fn collection_attribute() -> Weight; + fn can_transfer() -> Weight; + fn collections(n: u32) -> Weight; + fn items(n: u32) -> Weight; + fn owned(n: u32) -> Weight; + fn owned_in_collection(n: u32) -> Weight; +} + +/// Weights for pallet-uniques chain-extension +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + fn owner() -> Weight { + T::DbWeight::get().reads(1 as u64) + } + + fn collection_owner() -> Weight { + T::DbWeight::get().reads(1 as u64) + } + + fn attribute() -> Weight { + T::DbWeight::get().reads(1 as u64) + } + + fn collection_attribute() -> Weight { + T::DbWeight::get().reads(1 as u64) + } + + fn can_transfer() -> Weight { + T::DbWeight::get().reads(2 as u64) + } + + fn collections(n: u32) -> Weight { + T::DbWeight::get().reads(1 as u64).saturating_mul(n.into()) + } + + fn items(n: u32) -> Weight { + T::DbWeight::get().reads(1 as u64).saturating_mul(n.into()) + } + + fn owned(n: u32) -> Weight { + T::DbWeight::get().reads(1 as u64).saturating_mul(n.into()) + } + + fn owned_in_collection(n: u32) -> Weight { + T::DbWeight::get().reads(1 as u64).saturating_mul(n.into()) + } +} diff --git a/chain-extensions/types/uniques/Cargo.toml b/chain-extensions/types/uniques/Cargo.toml new file mode 100644 index 0000000000..d5c386bcd5 --- /dev/null +++ b/chain-extensions/types/uniques/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "uniques-chain-extension-types" +version = "0.1.0" +license = "Apache-2.0" +description = "Types definitions for uniques chain-extension" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } + +frame-system = { workspace = true } +pallet-contracts = { workspace = true } + +[features] +default = ["std"] +std = [ + "scale-info/std", + "parity-scale-codec/std", + "pallet-contracts/std", + "frame-system/std", +] diff --git a/chain-extensions/types/uniques/src/lib.rs b/chain-extensions/types/uniques/src/lib.rs new file mode 100644 index 0000000000..675562901f --- /dev/null +++ b/chain-extensions/types/uniques/src/lib.rs @@ -0,0 +1,37 @@ +#![cfg_attr(not(feature = "std"), no_std)] +use parity_scale_codec::MaxEncodedLen; +use parity_scale_codec::{Decode, Encode}; + +#[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, Debug)] +#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] +pub enum Outcome { + /// Success + Success = 0, + /// Origin Caller is not supported + OriginCannotBeCaller = 98, + /// Unknown error + RuntimeError = 99, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Encode, Decode, MaxEncodedLen)] +#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] +pub enum Origin { + Caller, + Address, +} + +impl Default for Origin { + fn default() -> Self { + Self::Address + } +} + +#[macro_export] +macro_rules! select_origin { + ($origin:expr, $account:expr) => { + match $origin { + Origin::Caller => return Ok(RetVal::Converging(Outcome::OriginCannotBeCaller as u32)), + Origin::Address => RawOrigin::Signed($account), + } + }; +} diff --git a/runtime/local/Cargo.toml b/runtime/local/Cargo.toml index d2f846ae7f..431875ce82 100644 --- a/runtime/local/Cargo.toml +++ b/runtime/local/Cargo.toml @@ -96,6 +96,7 @@ moonbeam-rpc-primitives-txpool = { workspace = true, optional = true } # chain-extensions pallet-chain-extension-assets = { workspace = true } +pallet-chain-extension-uniques = { workspace = true } # benchmarking array-bytes = { workspace = true } @@ -184,6 +185,7 @@ std = [ "moonbeam-rpc-primitives-txpool/std", "substrate-wasm-builder", "pallet-chain-extension-assets/std", + "pallet-chain-extension-uniques/std", "astar-primitives/std", ] runtime-benchmarks = [ diff --git a/runtime/local/src/chain_extensions.rs b/runtime/local/src/chain_extensions.rs index a368fa8d18..b1cb947773 100644 --- a/runtime/local/src/chain_extensions.rs +++ b/runtime/local/src/chain_extensions.rs @@ -20,6 +20,7 @@ use super::{Runtime, UnifiedAccounts, Xvm}; /// Registered WASM contracts chain extensions. pub use pallet_chain_extension_assets::AssetsExtension; +pub use pallet_chain_extension_uniques::UniquesExtension; use pallet_contracts::chain_extension::RegisteredChainExtension; pub use pallet_chain_extension_unified_accounts::UnifiedAccountsExtension; @@ -40,3 +41,9 @@ impl RegisteredChainExten impl RegisteredChainExtension for UnifiedAccountsExtension { const ID: u16 = 03; } + +impl RegisteredChainExtension + for UniquesExtension +{ + const ID: u16 = 04; +} diff --git a/runtime/local/src/lib.rs b/runtime/local/src/lib.rs index 611cb78485..e5ef9e19a7 100644 --- a/runtime/local/src/lib.rs +++ b/runtime/local/src/lib.rs @@ -930,6 +930,7 @@ impl pallet_contracts::Config for Runtime { XvmExtension, AssetsExtension>, UnifiedAccountsExtension, + UniquesExtension>, ); type Schedule = Schedule; type AddressGenerator = pallet_contracts::DefaultAddressGenerator; diff --git a/runtime/shibuya/Cargo.toml b/runtime/shibuya/Cargo.toml index 4519a2db5f..8393491ee2 100644 --- a/runtime/shibuya/Cargo.toml +++ b/runtime/shibuya/Cargo.toml @@ -130,6 +130,7 @@ moonbeam-rpc-primitives-txpool = { workspace = true, optional = true } # chain-extensions pallet-chain-extension-assets = { workspace = true } +pallet-chain-extension-uniques = { workspace = true } # benchmarking array-bytes = { workspace = true } @@ -242,6 +243,7 @@ std = [ "xcm-executor/std", "substrate-wasm-builder", "pallet-chain-extension-assets/std", + "pallet-chain-extension-uniques/std", "orml-xtokens/std", "orml-xcm-support/std", "astar-primitives/std", diff --git a/runtime/shibuya/src/chain_extensions.rs b/runtime/shibuya/src/chain_extensions.rs index a368fa8d18..b1cb947773 100644 --- a/runtime/shibuya/src/chain_extensions.rs +++ b/runtime/shibuya/src/chain_extensions.rs @@ -20,6 +20,7 @@ use super::{Runtime, UnifiedAccounts, Xvm}; /// Registered WASM contracts chain extensions. pub use pallet_chain_extension_assets::AssetsExtension; +pub use pallet_chain_extension_uniques::UniquesExtension; use pallet_contracts::chain_extension::RegisteredChainExtension; pub use pallet_chain_extension_unified_accounts::UnifiedAccountsExtension; @@ -40,3 +41,9 @@ impl RegisteredChainExten impl RegisteredChainExtension for UnifiedAccountsExtension { const ID: u16 = 03; } + +impl RegisteredChainExtension + for UniquesExtension +{ + const ID: u16 = 04; +} diff --git a/runtime/shibuya/src/lib.rs b/runtime/shibuya/src/lib.rs index d9f199a9d3..7c4b74d4aa 100644 --- a/runtime/shibuya/src/lib.rs +++ b/runtime/shibuya/src/lib.rs @@ -719,6 +719,7 @@ impl pallet_contracts::Config for Runtime { XvmExtension, AssetsExtension>, UnifiedAccountsExtension, + UniquesExtension>, ); type Schedule = Schedule; type AddressGenerator = pallet_contracts::DefaultAddressGenerator;