From 95c3ee10bc90cb0cb9729aa545b962e3c006ce42 Mon Sep 17 00:00:00 2001
From: dzmitry-lahoda <dzmitry@lahoda.pro>
Date: Thu, 7 Dec 2023 16:40:16 +0000
Subject: [PATCH] feat(xcm): support json schema (for CosmWasm VM support)
 (#1454)

# Description

- What does this PR do? Allows to generate JSON schema for subset of XCM
in std builds
- Why are these changes needed? To support XCM messages in CosmWasm
contracts which require Schemars to generate contract clients
- How were these changes implemented and what do they affect? We will
use schema feature flag to build XCM pallet with JSON schema enabled

# Checklist

- [x] My PR includes a detailed description as outlined in the
"Description" section above
- [x] My PR follows the [labeling requirements](CONTRIBUTING.md#Process)
of this project (at minimum one label for `T`
  required)
- [x] I have made corresponding changes to the documentation (if
applicable)
- [x] I have added tests that prove my fix is effective or that my
feature works (if applicable)
- [x] If this PR alters any external APIs or interfaces used by
Polkadot, the corresponding Polkadot PR is ready as well
  as the corresponding Cumulus PR (optional)
---
 Cargo.lock                                    | 42 ++++++++++++++++++-
 polkadot/xcm/Cargo.toml                       |  4 +-
 polkadot/xcm/src/double_encoded.rs            |  1 +
 polkadot/xcm/src/v2/mod.rs                    |  1 +
 polkadot/xcm/src/v3/junction.rs               |  4 ++
 polkadot/xcm/src/v3/junctions.rs              |  1 +
 polkadot/xcm/src/v3/mod.rs                    | 10 +++++
 polkadot/xcm/src/v3/multiasset.rs             |  8 ++++
 polkadot/xcm/src/v3/multilocation.rs          |  1 +
 polkadot/xcm/src/v3/traits.rs                 |  1 +
 prdoc/pr_1454.prdoc                           | 10 +++++
 substrate/primitives/weights/Cargo.toml       |  5 +++
 substrate/primitives/weights/src/weight_v2.rs |  1 +
 13 files changed, 86 insertions(+), 3 deletions(-)
 create mode 100644 prdoc/pr_1454.prdoc

diff --git a/Cargo.lock b/Cargo.lock
index 2e0de32bc6e..7f414cfff6f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1521,13 +1521,14 @@ dependencies = [
 
 [[package]]
 name = "bounded-collections"
-version = "0.1.8"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb5b05133427c07c4776906f673ccf36c21b102c9829c641a5b56bd151d44fd6"
+checksum = "ca548b6163b872067dc5eb82fd130c56881435e30367d2073594a3d9744120dd"
 dependencies = [
  "log",
  "parity-scale-codec",
  "scale-info",
+ "schemars",
  "serde",
 ]
 
@@ -16344,6 +16345,30 @@ dependencies = [
  "windows-sys 0.48.0",
 ]
 
+[[package]]
+name = "schemars"
+version = "0.8.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "763f8cd0d4c71ed8389c90cb8100cba87e763bd01a8e614d4f0af97bcd50a161"
+dependencies = [
+ "dyn-clone",
+ "schemars_derive",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "schemars_derive"
+version = "0.8.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0f696e21e10fa546b7ffb1c9672c6de8fbc7a81acf59524386d8639bf12737"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde_derive_internals",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "schnellru"
 version = "0.2.1"
@@ -16614,6 +16639,17 @@ dependencies = [
  "syn 2.0.39",
 ]
 
+[[package]]
+name = "serde_derive_internals"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "serde_fmt"
 version = "1.0.3"
@@ -18117,6 +18153,7 @@ dependencies = [
  "bounded-collections",
  "parity-scale-codec",
  "scale-info",
+ "schemars",
  "serde",
  "smallvec",
  "sp-arithmetic",
@@ -18353,6 +18390,7 @@ dependencies = [
  "log",
  "parity-scale-codec",
  "scale-info",
+ "schemars",
  "serde",
  "sp-io",
  "sp-weights",
diff --git a/polkadot/xcm/Cargo.toml b/polkadot/xcm/Cargo.toml
index 8fa0d128950..3d05957a3fe 100644
--- a/polkadot/xcm/Cargo.toml
+++ b/polkadot/xcm/Cargo.toml
@@ -7,7 +7,7 @@ edition.workspace = true
 license.workspace = true
 
 [dependencies]
-bounded-collections = { version = "0.1.8", default-features = false, features = ["serde"] }
+bounded-collections = { version = "0.1.9", default-features = false, features = ["serde"] }
 derivative = { version = "2.2.0", default-features = false, features = ["use_core"] }
 impl-trait-for-tuples = "0.2.2"
 log = { version = "0.4.17", default-features = false }
@@ -15,6 +15,7 @@ parity-scale-codec = { version = "3.6.1", default-features = false, features = [
 scale-info = { version = "2.10.0", default-features = false, features = ["derive", "serde"] }
 sp-weights = { path = "../../substrate/primitives/weights", default-features = false, features = ["serde"] }
 serde = { version = "1.0.193", default-features = false, features = ["alloc", "derive"] }
+schemars = { version = "0.8.13", default-features = true, optional = true }
 xcm-procedural = { path = "procedural" }
 environmental = { version = "1.1.4", default-features = false }
 
@@ -35,3 +36,4 @@ std = [
 	"serde/std",
 	"sp-weights/std",
 ]
+json-schema = ["bounded-collections/json-schema", "dep:schemars", "sp-weights/json-schema"]
diff --git a/polkadot/xcm/src/double_encoded.rs b/polkadot/xcm/src/double_encoded.rs
index 875b811da3f..45856f657d1 100644
--- a/polkadot/xcm/src/double_encoded.rs
+++ b/polkadot/xcm/src/double_encoded.rs
@@ -25,6 +25,7 @@ use parity_scale_codec::{Decode, DecodeLimit, Encode};
 #[codec(decode_bound())]
 #[scale_info(bounds(), skip_type_params(T))]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 pub struct DoubleEncoded<T> {
 	encoded: Vec<u8>,
 	#[codec(skip)]
diff --git a/polkadot/xcm/src/v2/mod.rs b/polkadot/xcm/src/v2/mod.rs
index 7f654ebfd9e..188b7f0b5c9 100644
--- a/polkadot/xcm/src/v2/mod.rs
+++ b/polkadot/xcm/src/v2/mod.rs
@@ -82,6 +82,7 @@ pub use traits::{Error, ExecuteXcm, GetWeight, Outcome, Result, SendError, SendR
 /// Basically just the XCM (more general) version of `ParachainDispatchOrigin`.
 #[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 pub enum OriginKind {
 	/// Origin should just be the native dispatch origin representation for the sender in the
 	/// local runtime framework. For Cumulus/Frame chains this is the `Parachain` or `Relay` origin
diff --git a/polkadot/xcm/src/v3/junction.rs b/polkadot/xcm/src/v3/junction.rs
index 5e6e4ab903b..6ae339db2ae 100644
--- a/polkadot/xcm/src/v3/junction.rs
+++ b/polkadot/xcm/src/v3/junction.rs
@@ -49,6 +49,7 @@ use serde::{Deserialize, Serialize};
 	Serialize,
 	Deserialize,
 )]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
 pub enum NetworkId {
 	/// Network specified by the first 32 bytes of its genesis block.
@@ -119,6 +120,7 @@ impl TryFrom<OldNetworkId> for NetworkId {
 	Serialize,
 	Deserialize,
 )]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
 pub enum BodyId {
 	/// The only body in its context.
@@ -190,6 +192,7 @@ impl TryFrom<OldBodyId> for BodyId {
 	Serialize,
 	Deserialize,
 )]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
 pub enum BodyPart {
 	/// The body's declaration, under whatever means it decides.
@@ -266,6 +269,7 @@ impl TryFrom<OldBodyPart> for BodyPart {
 	Serialize,
 	Deserialize,
 )]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
 pub enum Junction {
 	/// An indexed parachain belonging to and operated by the context.
diff --git a/polkadot/xcm/src/v3/junctions.rs b/polkadot/xcm/src/v3/junctions.rs
index d1cbc2dbed4..88da20cb1a1 100644
--- a/polkadot/xcm/src/v3/junctions.rs
+++ b/polkadot/xcm/src/v3/junctions.rs
@@ -44,6 +44,7 @@ pub(crate) const MAX_JUNCTIONS: usize = 8;
 	serde::Serialize,
 	serde::Deserialize,
 )]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
 pub enum Junctions {
 	/// The interpreting consensus system.
diff --git a/polkadot/xcm/src/v3/mod.rs b/polkadot/xcm/src/v3/mod.rs
index bbdd504ceb0..50b7a539122 100644
--- a/polkadot/xcm/src/v3/mod.rs
+++ b/polkadot/xcm/src/v3/mod.rs
@@ -69,6 +69,7 @@ pub type QueryId = u64;
 #[codec(encode_bound())]
 #[scale_info(bounds(), skip_type_params(Call))]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 pub struct Xcm<Call>(pub Vec<Instruction<Call>>);
 
 /// The maximal number of instructions in an XCM before decoding fails.
@@ -232,15 +233,19 @@ pub mod prelude {
 }
 
 parameter_types! {
+	#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 	pub MaxPalletNameLen: u32 = 48;
 	/// Maximum size of the encoded error code coming from a `Dispatch` result, used for
 	/// `MaybeErrorCode`. This is not (yet) enforced, so it's just an indication of expectation.
+	#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 	pub MaxDispatchErrorLen: u32 = 128;
+	#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 	pub MaxPalletsInfo: u32 = 64;
 }
 
 #[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 pub struct PalletInfo {
 	#[codec(compact)]
 	pub index: u32,
@@ -272,6 +277,7 @@ impl PalletInfo {
 
 #[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 pub enum MaybeErrorCode {
 	Success,
 	Error(BoundedVec<u8, MaxDispatchErrorLen>),
@@ -296,6 +302,7 @@ impl Default for MaybeErrorCode {
 /// Response data to a query.
 #[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 pub enum Response {
 	/// No response. Serves as a neutral default.
 	Null,
@@ -320,6 +327,7 @@ impl Default for Response {
 /// Information regarding the composition of a query response.
 #[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 pub struct QueryResponseInfo {
 	/// The destination to which the query response message should be send.
 	pub destination: MultiLocation,
@@ -333,6 +341,7 @@ pub struct QueryResponseInfo {
 /// An optional weight limit.
 #[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 pub enum WeightLimit {
 	/// No weight limit imposed.
 	Unlimited,
@@ -417,6 +426,7 @@ impl XcmContext {
 #[codec(decode_bound())]
 #[scale_info(bounds(), skip_type_params(Call))]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 pub enum Instruction<Call> {
 	/// Withdraw asset(s) (`assets`) from the ownership of `origin` and place them into the Holding
 	/// Register.
diff --git a/polkadot/xcm/src/v3/multiasset.rs b/polkadot/xcm/src/v3/multiasset.rs
index 454120a1a7b..c8801f5a461 100644
--- a/polkadot/xcm/src/v3/multiasset.rs
+++ b/polkadot/xcm/src/v3/multiasset.rs
@@ -47,6 +47,7 @@ use scale_info::TypeInfo;
 	Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen,
 )]
 #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
 pub enum AssetInstance {
 	/// Undefined - used if the non-fungible asset class has only one instance.
@@ -243,6 +244,7 @@ impl TryFrom<AssetInstance> for u128 {
 /// instance.
 #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, TypeInfo, MaxEncodedLen)]
 #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
 pub enum Fungibility {
 	/// A fungible asset; we record a number of units, as a `u128` in the inner item.
@@ -313,6 +315,7 @@ impl TryFrom<OldFungibility> for Fungibility {
 	Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen,
 )]
 #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
 pub enum WildFungibility {
 	/// The asset is fungible.
@@ -337,6 +340,7 @@ impl TryFrom<OldWildFungibility> for WildFungibility {
 	Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen,
 )]
 #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
 pub enum AssetId {
 	/// A specific location identifying an asset.
@@ -412,6 +416,7 @@ impl AssetId {
 /// Either an amount of a single fungible asset, or a single well-identified non-fungible asset.
 #[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
 #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
 pub struct MultiAsset {
 	/// The overall asset identity (aka *class*, in the case of a non-fungible).
@@ -510,6 +515,7 @@ impl TryFrom<OldMultiAsset> for MultiAsset {
 /// - The number of items should grow no larger than `MAX_ITEMS_IN_MULTIASSETS`.
 #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, TypeInfo, Default)]
 #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
 pub struct MultiAssets(Vec<MultiAsset>);
 
@@ -710,6 +716,7 @@ impl MultiAssets {
 /// A wildcard representing a set of assets.
 #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
 #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
 pub enum WildMultiAsset {
 	/// All assets in Holding.
@@ -823,6 +830,7 @@ impl<A: Into<AssetId>, B: Into<WildFungibility>> From<(A, B)> for WildMultiAsset
 /// `MultiAsset` collection, defined either by a number of `MultiAssets` or a single wildcard.
 #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
 #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
 pub enum MultiAssetFilter {
 	/// Specify the filter as being everything contained by the given `MultiAssets` inner.
diff --git a/polkadot/xcm/src/v3/multilocation.rs b/polkadot/xcm/src/v3/multilocation.rs
index 89e25984443..9649b1b3207 100644
--- a/polkadot/xcm/src/v3/multilocation.rs
+++ b/polkadot/xcm/src/v3/multilocation.rs
@@ -66,6 +66,7 @@ use scale_info::TypeInfo;
 	serde::Serialize,
 	serde::Deserialize,
 )]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 pub struct MultiLocation {
 	/// The number of parent junctions at the beginning of this `MultiLocation`.
 	pub parents: u8,
diff --git a/polkadot/xcm/src/v3/traits.rs b/polkadot/xcm/src/v3/traits.rs
index 6054bf1456a..29bd40a6a2d 100644
--- a/polkadot/xcm/src/v3/traits.rs
+++ b/polkadot/xcm/src/v3/traits.rs
@@ -30,6 +30,7 @@ use super::*;
 /// they will retain the same index over time.
 #[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)]
 #[scale_info(replace_segment("staging_xcm", "xcm"))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 pub enum Error {
 	// Errors that happen due to instructions being executed. These alone are defined in the
 	// XCM specification.
diff --git a/prdoc/pr_1454.prdoc b/prdoc/pr_1454.prdoc
new file mode 100644
index 00000000000..c96bfcde6cf
--- /dev/null
+++ b/prdoc/pr_1454.prdoc
@@ -0,0 +1,10 @@
+title: Support XCM as part of Cosmos CosmWasm contract messages
+
+doc:
+  - audience: Runtime Dev
+    description: |
+      Made XCM JSON schema behind flag, bumped bounded-collection so to ensure it has that flag too.
+
+crates:
+  - name: staging-xcm
+  - name: sp-weights
diff --git a/substrate/primitives/weights/Cargo.toml b/substrate/primitives/weights/Cargo.toml
index 163b241276c..fb2e2ae8cb1 100644
--- a/substrate/primitives/weights/Cargo.toml
+++ b/substrate/primitives/weights/Cargo.toml
@@ -21,6 +21,7 @@ smallvec = "1.11.0"
 sp-arithmetic = { path = "../arithmetic", default-features = false }
 sp-debug-derive = { path = "../debug-derive", default-features = false }
 sp-std = { path = "../std", default-features = false }
+schemars = { version = "0.8.3", default-features = false, optional = true }
 
 [features]
 default = ["std"]
@@ -44,3 +45,7 @@ serde = [
 	"scale-info/serde",
 	"sp-arithmetic/serde",
 ]
+
+json-schema = [
+	"dep:schemars",
+]
diff --git a/substrate/primitives/weights/src/weight_v2.rs b/substrate/primitives/weights/src/weight_v2.rs
index d692aaff8f5..3c10929f433 100644
--- a/substrate/primitives/weights/src/weight_v2.rs
+++ b/substrate/primitives/weights/src/weight_v2.rs
@@ -23,6 +23,7 @@ use super::*;
 
 #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Eq, PartialEq, Copy, Clone, Debug, Default)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
 pub struct Weight {
 	#[codec(compact)]
 	/// The weight of computational time used based on some reference hardware.
-- 
GitLab