diff --git a/Cargo.lock b/Cargo.lock index 5d8bf23583..8eb9169986 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -99,9 +99,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" +checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" [[package]] name = "approx" @@ -279,7 +279,7 @@ dependencies = [ "memchr", "num_cpus", "once_cell", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "pin-utils", "slab", "wasm-bindgen-futures", @@ -327,7 +327,7 @@ dependencies = [ "futures-sink", "futures-util", "memchr", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", ] [[package]] @@ -384,7 +384,7 @@ dependencies = [ "futures-core", "getrandom 0.2.6", "instant", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "rand 0.8.5", "tokio", ] @@ -406,9 +406,9 @@ dependencies = [ [[package]] name = "base-x" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" +checksum = "dc19a4937b4fbd3fe3379793130e42060d10627a360f2127802b10b87e7baf74" [[package]] name = "base16ct" @@ -2023,6 +2023,34 @@ dependencies = [ "substrate-wasm-builder", ] +[[package]] +name = "did-rpc" +version = "1.6.2" +dependencies = [ + "did-rpc-runtime-api", + "jsonrpsee", + "parity-scale-codec", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-rpc", + "sp-runtime", +] + +[[package]] +name = "did-rpc-runtime-api" +version = "1.6.2" +dependencies = [ + "did", + "kilt-support", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-runtime", + "sp-std", +] + [[package]] name = "digest" version = "0.8.1" @@ -2819,7 +2847,7 @@ dependencies = [ "futures-io", "memchr", "parking", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "waker-fn", ] @@ -2877,7 +2905,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "pin-utils", "slab", ] @@ -3157,14 +3185,14 @@ checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" dependencies = [ "bytes", "http", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", ] [[package]] name = "httparse" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6330e8a36bd8c859f3fa6d9382911fbb7147ec39807f63b923933a247240b9ba" +checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" [[package]] name = "httpdate" @@ -3194,7 +3222,7 @@ dependencies = [ "httparse", "httpdate", "itoa 1.0.1", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "socket2", "tokio", "tower-service", @@ -3561,6 +3589,7 @@ dependencies = [ "cumulus-relay-chain-interface", "cumulus-relay-chain-rpc-interface", "derive_more", + "did-rpc", "frame-benchmarking", "frame-benchmarking-cli", "futures 0.3.21", @@ -3626,6 +3655,8 @@ dependencies = [ "frame-system", "parity-scale-codec", "scale-info", + "serde", + "serde_json", "sp-core", "sp-runtime", "sp-std", @@ -4442,9 +4473,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f35facd4a5673cb5a48822be2be1d4236c1c99cb4113cab7061ac720d5bf859" +checksum = "92e7e15d7610cce1d9752e137625f14e61a28cd45929b6e12e47b50fe154ee2e" dependencies = [ "cc", "pkg-config", @@ -4569,6 +4600,7 @@ name = "mashnet-node" version = "1.7.0" dependencies = [ "clap", + "did-rpc", "frame-benchmarking", "frame-benchmarking-cli", "frame-system", @@ -4625,6 +4657,7 @@ dependencies = [ "ctype", "delegation", "did", + "did-rpc-runtime-api", "frame-benchmarking", "frame-executive", "frame-support", @@ -6505,6 +6538,7 @@ dependencies = [ "pallet-balances", "parity-scale-codec", "scale-info", + "serde", "sp-core", "sp-io", "sp-keystore", @@ -6797,6 +6831,7 @@ dependencies = [ "cumulus-primitives-timestamp", "delegation", "did", + "did-rpc-runtime-api", "frame-benchmarking", "frame-executive", "frame-support", @@ -6960,9 +6995,9 @@ checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" [[package]] name = "pin-project-lite" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "pin-utils" @@ -8576,18 +8611,18 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300f2a835d808734ee295d45007adacb9ebb29dd3ae2424acfa17930cae541da" +checksum = "685d58625b6c2b83e4cc88a27c4bf65adb7b6b16dbdc413e515c9405b47432ab" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c38e3aecd2b21cb3959637b883bb3714bc7e43f0268b9a29d3743ee3e55cdd2" +checksum = "a043824e29c94169374ac5183ac0ed43f5724dc4556b19568007486bd840fa1f" dependencies = [ "proc-macro2", "quote", @@ -11176,6 +11211,7 @@ dependencies = [ "cumulus-primitives-timestamp", "delegation", "did", + "did-rpc-runtime-api", "frame-benchmarking", "frame-executive", "frame-support", @@ -11635,9 +11671,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] @@ -11661,7 +11697,7 @@ dependencies = [ "num_cpus", "once_cell", "parking_lot 0.12.0", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "signal-hook-registry", "socket2", "tokio-macros", @@ -11700,7 +11736,7 @@ dependencies = [ "futures-core", "futures-io", "futures-sink", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "tokio", "tracing", ] @@ -11727,16 +11763,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" dependencies = [ "cfg-if 1.0.0", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" +checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" dependencies = [ "proc-macro2", "quote", @@ -11981,9 +12017,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" diff --git a/Cargo.toml b/Cargo.toml index 89ae7cf8b4..040d093c24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,8 @@ panic = "unwind" members = [ "nodes/*", "pallets/*", + "rpc/did", + "rpc/did/runtime-api", "runtimes/*", "support", ] diff --git a/nodes/parachain/Cargo.toml b/nodes/parachain/Cargo.toml index e656176447..c52921540b 100644 --- a/nodes/parachain/Cargo.toml +++ b/nodes/parachain/Cargo.toml @@ -16,6 +16,7 @@ substrate-build-script-utils = {git = "https://github.com/paritytech/substrate", [dependencies] # Internal dependencies +did-rpc = {path = "../../rpc/did"} peregrine-runtime = {path = "../../runtimes/peregrine"} runtime-common = {path = "../../runtimes/common"} spiritnet-runtime = {path = "../../runtimes/spiritnet"} @@ -37,15 +38,15 @@ frame-rpc-system = {package = "substrate-frame-rpc-system", git = "https://githu pallet-transaction-payment-rpc = {git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24"} sc-basic-authorship = {git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24"} sc-chain-spec = {git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24"} -sc-cli = {git = "https://github.com/paritytech/substrate", features = ["wasmtime"] , branch = "polkadot-v0.9.24"} +sc-cli = {git = "https://github.com/paritytech/substrate", features = ["wasmtime"], branch = "polkadot-v0.9.24"} sc-client-api = {git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24"} sc-consensus = {git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24"} -sc-executor = {git = "https://github.com/paritytech/substrate", features = ["wasmtime"] , branch = "polkadot-v0.9.24"} +sc-executor = {git = "https://github.com/paritytech/substrate", features = ["wasmtime"], branch = "polkadot-v0.9.24"} sc-keystore = {git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24"} sc-network = {git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24"} sc-rpc = {git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24"} sc-rpc-api = {git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24"} -sc-service = {git = "https://github.com/paritytech/substrate", features = ["wasmtime"] , branch = "polkadot-v0.9.24"} +sc-service = {git = "https://github.com/paritytech/substrate", features = ["wasmtime"], branch = "polkadot-v0.9.24"} sc-sysinfo = {git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24"} sc-telemetry = {git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24"} sc-tracing = {git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24"} diff --git a/nodes/parachain/src/rpc.rs b/nodes/parachain/src/rpc.rs index 6aeee65588..bd0aa872b5 100644 --- a/nodes/parachain/src/rpc.rs +++ b/nodes/parachain/src/rpc.rs @@ -25,17 +25,15 @@ use std::sync::Arc; -use polkadot_service::AuxStore; -use runtime_common::{AccountId, Balance, Block, Index}; +use jsonrpsee::RpcModule; + +pub use sc_rpc_api::DenyUnsafe; use sc_transaction_pool_api::TransactionPool; use sp_api::ProvideRuntimeApi; use sp_block_builder::BlockBuilder; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; -pub use sc_rpc_api::DenyUnsafe; - -/// A type representing all RPC extensions. -pub type RpcExtension = jsonrpsee::RpcModule<()>; +use runtime_common::{AccountId, Balance, Block, BlockNumber, DidIdentifier, Hash, Index}; /// Full client dependencies. pub struct FullDeps { @@ -48,21 +46,22 @@ pub struct FullDeps { } /// Instantiate all full RPC extensions. -pub fn create_full(deps: FullDeps) -> Result> +pub fn create_full(deps: FullDeps) -> Result, Box> where C: ProvideRuntimeApi, - C: AuxStore, C: HeaderBackend + HeaderMetadata + 'static, C: Send + Sync + 'static, C::Api: frame_rpc_system::AccountNonceApi, C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, C::Api: BlockBuilder, - P: TransactionPool + Sync + Send + 'static, + C::Api: did_rpc::DidRuntimeApi, + P: TransactionPool + 'static, { + use did_rpc::{DidApiServer, DidQuery}; use frame_rpc_system::{System, SystemApiServer}; use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; - let mut module = RpcExtension::new(()); + let mut module = RpcModule::new(()); let FullDeps { client, pool, @@ -70,13 +69,13 @@ where } = deps; module.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?; - module.merge(TransactionPayment::new(client).into_rpc())?; - + module.merge(TransactionPayment::new(client.clone()).into_rpc())?; // Extend this RPC with a custom API by using the following syntax. // `YourRpcStruct` should have a reference to a client, which is needed // to call into the runtime. - // `module.merge(YourRpcTrait::into_rpc(YourRpcStruct::new(ReferenceToClient, - // ...)))?;` + // + // `module.merge(YourRpcStruct::new(ReferenceToClient).into_rpc())?;` + module.merge(DidQuery::new(client).into_rpc())?; Ok(module) } diff --git a/nodes/parachain/src/service.rs b/nodes/parachain/src/service.rs index bb1a4fb838..115a49df1c 100644 --- a/nodes/parachain/src/service.rs +++ b/nodes/parachain/src/service.rs @@ -42,7 +42,7 @@ use sp_runtime::traits::BlakeTwo256; use std::{sync::Arc, time::Duration}; use substrate_prometheus_endpoint::Registry; -use runtime_common::{AccountId, AuthorityId, Balance, BlockNumber, Index}; +use runtime_common::{AccountId, AuthorityId, Balance, BlockNumber, DidIdentifier, Index}; type Header = sp_runtime::generic::Header; pub type Block = sp_runtime::generic::Block; @@ -237,7 +237,8 @@ where + sp_block_builder::BlockBuilder + cumulus_primitives_core::CollectCollationInfo + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi - + frame_rpc_system::AccountNonceApi, + + frame_rpc_system::AccountNonceApi + + did_rpc::DidRuntimeApi, sc_client_api::StateBackendFor, Block>: sp_api::StateBackend, Executor: sc_executor::NativeExecutionDispatch + 'static, RB: FnOnce( @@ -479,7 +480,8 @@ where + frame_rpc_system::AccountNonceApi + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi + sp_consensus_aura::AuraApi - + cumulus_primitives_core::CollectCollationInfo, + + cumulus_primitives_core::CollectCollationInfo + + did_rpc::DidRuntimeApi, sc_client_api::StateBackendFor, Block>: sp_api::StateBackend, { start_node_impl::( diff --git a/nodes/standalone/Cargo.toml b/nodes/standalone/Cargo.toml index fa70ddadbb..d738e34be7 100644 --- a/nodes/standalone/Cargo.toml +++ b/nodes/standalone/Cargo.toml @@ -15,6 +15,7 @@ substrate-build-script-utils = {git = "https://github.com/paritytech/substrate", [dependencies] # Internal dependencies +did-rpc = {path = "../../rpc/did"} mashnet-node-runtime = {path = "../../runtimes/standalone"} runtime-common = {path = "../../runtimes/common"} diff --git a/nodes/standalone/src/rpc.rs b/nodes/standalone/src/rpc.rs index 52487ba89f..d0144fb6b2 100644 --- a/nodes/standalone/src/rpc.rs +++ b/nodes/standalone/src/rpc.rs @@ -26,8 +26,8 @@ use std::sync::Arc; use jsonrpsee::RpcModule; -use mashnet_node_runtime::opaque::Block; -use runtime_common::{AccountId, Balance, Index}; + +use runtime_common::{AccountId, Balance, Block, BlockNumber, DidIdentifier, Hash, Index}; pub use sc_rpc_api::DenyUnsafe; use sc_transaction_pool_api::TransactionPool; use sp_api::ProvideRuntimeApi; @@ -53,8 +53,10 @@ where C::Api: frame_rpc_system::AccountNonceApi, C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, C::Api: BlockBuilder, + C::Api: did_rpc::DidRuntimeApi, P: TransactionPool + 'static, { + use did_rpc::{DidApiServer, DidQuery}; use frame_rpc_system::{System, SystemApiServer}; use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; @@ -66,13 +68,14 @@ where } = deps; module.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?; - module.merge(TransactionPayment::new(client).into_rpc())?; + module.merge(TransactionPayment::new(client.clone()).into_rpc())?; // Extend this RPC with a custom API by using the following syntax. // `YourRpcStruct` should have a reference to a client, which is needed // to call into the runtime. - // `module.merge(YourRpcTrait::into_rpc(YourRpcStruct::new(ReferenceToClient, - // ...)))?;` + // + // `module.merge(YourRpcStruct::new(ReferenceToClient).into_rpc())?;` + module.merge(DidQuery::new(client).into_rpc())?; Ok(module) } diff --git a/pallets/did/Cargo.toml b/pallets/did/Cargo.toml index 8a34969600..6fa3d6f4a8 100644 --- a/pallets/did/Cargo.toml +++ b/pallets/did/Cargo.toml @@ -30,6 +30,7 @@ kilt-support = {default-features = false, path = "../../support"} # External dependencies hex = {default-features = false, features = ["alloc"], version = "0.4.3"} log = "0.4.17" +serde = {version = "1.0.136", optional = true, features = ["derive"]} codec = {package = "parity-scale-codec", version = "3.1.2", default-features = false, features = ["derive"]} frame-support = {branch = "polkadot-v0.9.24", default-features = false, git = "https://github.com/paritytech/substrate"} @@ -70,6 +71,7 @@ std = [ "log/std", "pallet-balances/std", "scale-info/std", + "serde", "sp-core/std", "sp-io/std", "sp-keystore/std", diff --git a/pallets/did/src/did_details.rs b/pallets/did/src/did_details.rs index d8dd578a76..cf1b88bc03 100644 --- a/pallets/did/src/did_details.rs +++ b/pallets/did/src/did_details.rs @@ -38,6 +38,7 @@ use crate::{ /// Types of verification keys a DID can control. #[derive(Clone, Decode, RuntimeDebug, Encode, Eq, Ord, PartialEq, PartialOrd, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub enum DidVerificationKey { /// An Ed25519 public key. Ed25519(ed25519::Public), @@ -89,6 +90,7 @@ impl From for DidVerificationKey { /// Types of encryption keys a DID can control. #[derive(Clone, Copy, Decode, RuntimeDebug, Encode, Eq, Ord, PartialEq, PartialOrd, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub enum DidEncryptionKey { /// An X25519 public key. X25519([u8; 32]), @@ -96,6 +98,7 @@ pub enum DidEncryptionKey { /// A general public key under the control of the DID. #[derive(Clone, Decode, RuntimeDebug, Encode, Eq, Ord, PartialEq, PartialOrd, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub enum DidPublicKey { /// A verification key, used to generate and verify signatures. PublicVerificationKey(DidVerificationKey), @@ -234,11 +237,12 @@ impl> DidVerifiableIdentifier for I { #[derive(Clone, RuntimeDebug, Decode, Encode, PartialEq, Ord, PartialOrd, Eq, TypeInfo, MaxEncodedLen)] #[scale_info(skip_type_params(T))] #[codec(mel_bound())] -pub struct DidPublicKeyDetails { +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct DidPublicKeyDetails { /// A public key the DID controls. pub key: DidPublicKey, /// The block number in which the verification key was added to the DID. - pub block_number: BlockNumberOf, + pub block_number: BlockNumber, } /// The details associated to a DID identity. @@ -249,16 +253,16 @@ pub struct DidPublicKeyDetails { pub struct DidDetails { /// The ID of the authentication key, used to authenticate DID-related /// operations. - pub(crate) authentication_key: KeyIdOf, + pub authentication_key: KeyIdOf, /// The set of the key agreement key IDs, which can be used to encrypt /// data addressed to the DID subject. - pub(crate) key_agreement_keys: DidKeyAgreementKeySet, + pub key_agreement_keys: DidKeyAgreementKeySet, /// \[OPTIONAL\] The ID of the delegation key, used to verify the /// signatures of the delegations created by the DID subject. - pub(crate) delegation_key: Option>, + pub delegation_key: Option>, /// \[OPTIONAL\] The ID of the attestation key, used to verify the /// signatures of the attestations created by the DID subject. - pub(crate) attestation_key: Option>, + pub attestation_key: Option>, /// The map of public keys, with the key label as /// the key map and the tuple (key, addition_block_number) as the map /// value. @@ -268,14 +272,14 @@ pub struct DidDetails { /// the old attestation keys that have been rotated, i.e., they cannot /// be used to create new attestations but can still be used to verify /// previously issued attestations. - pub(crate) public_keys: DidPublicKeyMap, + pub public_keys: DidPublicKeyMap, /// The counter used to avoid replay attacks, which is checked and /// updated upon each DID operation involving with the subject as the /// creator. - pub(crate) last_tx_counter: u64, + pub last_tx_counter: u64, /// The deposit that was taken to incentivise fair use of the on chain /// storage. - pub(crate) deposit: Deposit, BalanceOf>, + pub deposit: Deposit, BalanceOf>, } impl DidDetails { @@ -530,27 +534,17 @@ impl DidDetails { } /// Increase the tx counter of the DID. - pub fn increase_tx_counter(&mut self) { + pub fn increase_tx_counter(&mut self) -> u64 { // Since we have transaction mortality now, we can safely wrap nonces around. self.last_tx_counter = self.last_tx_counter.wrapping_add(1); - } - - /// Returns the last used tx counter for the DID. - pub fn get_tx_counter_value(&self) -> u64 { self.last_tx_counter } - - /// Set the DID tx counter to an arbitrary value. - #[cfg(any(feature = "mock", test))] - pub fn set_tx_counter(&mut self, value: u64) { - self.last_tx_counter = value; - } } pub(crate) type DidNewKeyAgreementKeySet = BoundedBTreeSet::MaxNewKeyAgreementKeys>; pub(crate) type DidKeyAgreementKeySet = BoundedBTreeSet, ::MaxTotalKeyAgreementKeys>; pub(crate) type DidPublicKeyMap = - BoundedBTreeMap, DidPublicKeyDetails, ::MaxPublicKeysPerDid>; + BoundedBTreeMap, DidPublicKeyDetails>, ::MaxPublicKeysPerDid>; /// The details of a new DID to create. #[derive(Clone, RuntimeDebug, Decode, Encode, PartialEq, TypeInfo)] diff --git a/pallets/did/src/lib.rs b/pallets/did/src/lib.rs index 2a862251a1..9553c1045c 100644 --- a/pallets/did/src/lib.rs +++ b/pallets/did/src/lib.rs @@ -177,8 +177,8 @@ pub mod pallet { #[pallet::origin] pub type Origin = DidRawOrigin, AccountIdOf>; + pub type BalanceOf = as Currency>>::Balance; pub(crate) type CurrencyOf = ::Currency; - pub(crate) type BalanceOf = as Currency>>::Balance; pub(crate) type NegativeImbalanceOf = <::Currency as Currency>>::NegativeImbalance; #[pallet::config] @@ -1131,7 +1131,7 @@ pub mod pallet { fn validate_counter_value(counter: u64, did_details: &DidDetails) -> Result<(), DidError> { // Verify that the operation counter is equal to the stored one + 1, // possibly wrapping around when u64::MAX is reached. - let expected_nonce_value = did_details.get_tx_counter_value().wrapping_add(1); + let expected_nonce_value = did_details.last_tx_counter.wrapping_add(1); ensure!( counter == expected_nonce_value, DidError::SignatureError(SignatureError::InvalidNonce) diff --git a/pallets/did/src/tests.rs b/pallets/did/src/tests.rs index 0311e82947..fb4b4fcb65 100644 --- a/pallets/did/src/tests.rs +++ b/pallets/did/src/tests.rs @@ -2871,10 +2871,7 @@ fn check_authentication_successful_operation_verification() { )); // Verify that the DID tx counter has increased let did_details = Did::get_did(&call_operation.operation.did).expect("DID should be present on chain."); - assert_eq!( - did_details.get_tx_counter_value(), - mock_did.get_tx_counter_value() + 1u64 - ); + assert_eq!(did_details.last_tx_counter, mock_did.last_tx_counter + 1u64); }); } @@ -2901,10 +2898,7 @@ fn check_attestation_successful_operation_verification() { )); // Verify that the DID tx counter has increased let did_details = Did::get_did(&call_operation.operation.did).expect("DID should be present on chain."); - assert_eq!( - did_details.get_tx_counter_value(), - mock_did.get_tx_counter_value() + 1u64 - ); + assert_eq!(did_details.last_tx_counter, mock_did.last_tx_counter + 1u64); }); } @@ -2934,10 +2928,7 @@ fn check_delegation_successful_operation_verification() { )); // Verify that the DID tx counter has increased let did_details = Did::get_did(&call_operation.operation.did).expect("DID should be present on chain."); - assert_eq!( - did_details.get_tx_counter_value(), - mock_did.get_tx_counter_value() + 1u64 - ); + assert_eq!(did_details.last_tx_counter, mock_did.last_tx_counter + 1u64); }); } @@ -2966,7 +2957,7 @@ fn check_tx_counter_wrap_operation_verification() { let did = get_did_identifier_from_sr25519_key(auth_key.public()); let mut mock_did = generate_base_did_details::(DidVerificationKey::from(auth_key.public())); - mock_did.set_tx_counter(u64::MAX); + mock_did.last_tx_counter = u64::MAX; let mut call_operation = generate_test_did_call(DidVerificationKeyRelationship::Authentication, did.clone(), ACCOUNT_00); @@ -2984,7 +2975,7 @@ fn check_tx_counter_wrap_operation_verification() { )); // Verify that the DID tx counter has wrapped around let did_details = Did::get_did(&call_operation.operation.did).expect("DID should be present on chain."); - assert_eq!(did_details.get_tx_counter_value(), 0u64); + assert_eq!(did_details.last_tx_counter, 0u64); }); } diff --git a/pallets/pallet-web3-names/Cargo.toml b/pallets/pallet-web3-names/Cargo.toml index 3cd3a5a78b..6c95a5852c 100644 --- a/pallets/pallet-web3-names/Cargo.toml +++ b/pallets/pallet-web3-names/Cargo.toml @@ -20,6 +20,7 @@ sp-keystore = {branch = "polkadot-v0.9.24", git = "https://github.com/paritytech [dependencies] codec = {package = "parity-scale-codec", version = "3.1.2", default-features = false, features = ["derive"]} scale-info = {version = "2.1.1", default-features = false, features = ["derive"]} +serde = {version = "1.0.136", optional = true} # KILT kilt-support = {default-features = false, path = "../../support"} @@ -46,6 +47,7 @@ runtime-benchmarks = [ ] std = [ + "serde", "codec/std", "frame-benchmarking/std", "frame-support/std", diff --git a/pallets/pallet-web3-names/src/web3_name.rs b/pallets/pallet-web3-names/src/web3_name.rs index bd2d2abc08..0bcf6c82a2 100644 --- a/pallets/pallet-web3-names/src/web3_name.rs +++ b/pallets/pallet-web3-names/src/web3_name.rs @@ -16,7 +16,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use sp_std::{fmt::Debug, marker::PhantomData, vec::Vec}; +use sp_std::{fmt::Debug, marker::PhantomData, ops::Deref, vec::Vec}; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ensure, sp_runtime::SaturatedConversion, traits::Get, BoundedVec, RuntimeDebug}; @@ -36,6 +36,20 @@ pub struct AsciiWeb3Name( PhantomData<(T, T::MinNameLength)>, ); +impl Deref for AsciiWeb3Name { + type Target = BoundedVec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From> for Vec { + fn from(name: AsciiWeb3Name) -> Self { + name.0.into_inner() + } +} + impl TryFrom> for AsciiWeb3Name { type Error = Error; diff --git a/rpc/did/Cargo.toml b/rpc/did/Cargo.toml new file mode 100644 index 0000000000..f62183d572 --- /dev/null +++ b/rpc/did/Cargo.toml @@ -0,0 +1,28 @@ +[package] +edition = "2021" +name = "did-rpc" +version = "1.6.2" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.2" } +jsonrpsee = { version = "0.13.1", features = ["server", "macros"] } + +did-rpc-runtime-api = {version = "1.6.2", path = "./runtime-api"} + +sp-api = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.24"} +sp-blockchain = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.24"} +sp-core = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.24"} +sp-rpc = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.24"} +sp-runtime = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.24"} + +[features] +default = ["std"] +std = [ + "codec/std", + "did-rpc-runtime-api/std", + "sp-api/std", + "sp-core/std", + "sp-runtime/std", +] diff --git a/rpc/did/runtime-api/Cargo.toml b/rpc/did/runtime-api/Cargo.toml new file mode 100644 index 0000000000..e5e253666c --- /dev/null +++ b/rpc/did/runtime-api/Cargo.toml @@ -0,0 +1,31 @@ +[package] +edition = "2021" +name = "did-rpc-runtime-api" +version = "1.6.2" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.2", default-features = false, features = ["derive"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +serde = { version = "1.0.136", optional = true, features = ["derive"] } + +sp-api = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.24"} +sp-runtime = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.24"} +sp-std = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.24"} + +did = {path = "../../../pallets/did", default-features = false} +kilt-support = {path = "../../../support", default-features = false} + +[features] +default = ["std"] +std = [ + "serde", + "codec/std", + "sp-api/std", + "sp-runtime/std", + "sp-std/std", + "scale-info/std", + "did/std", + "kilt-support/std", +] diff --git a/rpc/did/runtime-api/src/did_details.rs b/rpc/did/runtime-api/src/did_details.rs new file mode 100644 index 0000000000..e4e0972650 --- /dev/null +++ b/rpc/did/runtime-api/src/did_details.rs @@ -0,0 +1,70 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2022 BOTLabs GmbH + +// The KILT Blockchain 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. + +// The KILT Blockchain 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 this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; + +use codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; + +use did::{did_details::DidPublicKeyDetails, AccountIdOf, BalanceOf, BlockNumberOf, KeyIdOf}; +use kilt_support::deposit::Deposit; + +#[derive(Encode, Decode, TypeInfo, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr( + feature = "std", + serde(bound( + serialize = " + Balance: std::fmt::Display, + AccountId: Serialize, + Key: Serialize, + BlockNumber: Serialize", + deserialize = " + Balance: std::str::FromStr, + AccountId: Deserialize<'de>, + Key: Deserialize<'de>, + BlockNumber: Deserialize<'de>" + )) +)] +pub struct DidDetails { + pub authentication_key: Key, + pub key_agreement_keys: BTreeSet, + pub delegation_key: Option, + pub attestation_key: Option, + pub public_keys: BTreeMap>, + pub last_tx_counter: u64, + pub deposit: Deposit, +} + +impl From> + for DidDetails, BlockNumberOf, AccountIdOf, BalanceOf> +{ + fn from(did_details: did::did_details::DidDetails) -> Self { + Self { + authentication_key: did_details.authentication_key, + key_agreement_keys: did_details.key_agreement_keys.into(), + delegation_key: did_details.delegation_key, + attestation_key: did_details.attestation_key, + public_keys: did_details.public_keys.into(), + last_tx_counter: did_details.last_tx_counter, + deposit: did_details.deposit, + } + } +} diff --git a/rpc/did/runtime-api/src/lib.rs b/rpc/did/runtime-api/src/lib.rs new file mode 100644 index 0000000000..afa320bbb4 --- /dev/null +++ b/rpc/did/runtime-api/src/lib.rs @@ -0,0 +1,116 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2022 BOTLabs GmbH + +// The KILT Blockchain 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. + +// The KILT Blockchain 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 this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; + +use codec::{Codec, Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_std::vec::Vec; + +mod did_details; +mod service_endpoint; + +pub use did_details::*; +pub use service_endpoint::*; + +#[derive(Encode, Decode, TypeInfo, PartialEq)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr( + feature = "std", + serde(bound( + serialize = " + Balance: std::fmt::Display, + AccountId: Serialize, + Key: Serialize, + BlockNumber: Serialize, + DidIdentifier: Serialize, + Type: Serialize, + Url: Serialize, + Id: Serialize, + Web3Name: Serialize,", + deserialize = " + Balance: std::str::FromStr, + AccountId: Deserialize<'de>, + Key: Deserialize<'de>, + BlockNumber: Deserialize<'de>, + DidIdentifier: Deserialize<'de>, + Type: Deserialize<'de>, + Url: Deserialize<'de>, + Id: Deserialize<'de>, + Web3Name: Deserialize<'de>," + )) +)] +pub struct DidLinkedInfo< + DidIdentifier, + AccountId, + Web3Name, + Id, + Type, + Url, + Balance, + Key: Ord, + BlockNumber: MaxEncodedLen, +> { + pub identifier: DidIdentifier, + pub accounts: Vec, + pub w3n: Option, + pub service_endpoints: Vec>, + pub details: DidDetails, +} + +/// The DidLinkedInfo with a Web3Name represented as a byte array. +/// +/// This will be returned by the runtime and processed by the client side RPC +/// implementation. +pub type RawDidLinkedInfo = + DidLinkedInfo, Vec, Vec, Vec, Balance, Key, BlockNumber>; + +sp_api::decl_runtime_apis! { + /// The API to query account nonce (aka transaction index). + pub trait DidApi where + DidIdentifier: Codec, + AccountId: Codec, + BlockNumber: Codec + MaxEncodedLen, + Key: Codec, + Balance: Codec, + { + /// Given a web3name this returns: + /// * the DID + /// * public keys stored for the did + /// * the web3name (optional) + /// * associated accounts + /// * service endpoints + fn query_did_by_w3n(name: Vec) -> Option>; + /// Given an account address this returns: + /// * the DID + /// * public keys stored for the did + /// * the web3name (optional) + /// * associated accounts + /// * service endpoints + fn query_did_by_account_id(account: AccountId) -> Option>; + /// Given a did this returns: + /// * the DID + /// * public keys stored for the did + /// * the web3name (optional) + /// * associated accounts + /// * service endpoints + fn query_did(did: DidIdentifier) -> Option>; + } +} diff --git a/rpc/did/runtime-api/src/service_endpoint.rs b/rpc/did/runtime-api/src/service_endpoint.rs new file mode 100644 index 0000000000..6e47cbab2a --- /dev/null +++ b/rpc/did/runtime-api/src/service_endpoint.rs @@ -0,0 +1,48 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2022 BOTLabs GmbH + +// The KILT Blockchain 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. + +// The KILT Blockchain 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 this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_std::vec::Vec; + +#[derive(Encode, Decode, TypeInfo, PartialEq)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct ServiceEndpoint { + pub id: Id, + pub service_types: Vec, + pub urls: Vec, +} + +impl From> for ServiceEndpoint, Vec, Vec> { + fn from(runtime_endpoint: did::service_endpoints::DidEndpoint) -> Self { + ServiceEndpoint { + id: runtime_endpoint.id.into_inner(), + service_types: runtime_endpoint + .service_types + .into_inner() + .into_iter() + .map(|v| v.into_inner()) + .collect(), + urls: runtime_endpoint + .urls + .into_inner() + .into_iter() + .map(|v| v.into_inner()) + .collect(), + } + } +} diff --git a/rpc/did/src/lib.rs b/rpc/did/src/lib.rs new file mode 100644 index 0000000000..9e9a97b12f --- /dev/null +++ b/rpc/did/src/lib.rs @@ -0,0 +1,249 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2022 BOTLabs GmbH + +// The KILT Blockchain 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. + +// The KILT Blockchain 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 this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use std::{fmt::Display, str::FromStr, sync::Arc}; + +use codec::{Codec, MaxEncodedLen}; +use did_rpc_runtime_api::{DidLinkedInfo, ServiceEndpoint}; +use jsonrpsee::{ + core::{async_trait, RpcResult}, + proc_macros::rpc, + types::error::{CallError, ErrorObject}, +}; +use sp_api::ProvideRuntimeApi; +use sp_blockchain::HeaderBackend; +use sp_runtime::{generic::BlockId, traits::Block as BlockT}; + +pub use did_rpc_runtime_api::DidApi as DidRuntimeApi; + +pub type RpcDidLinkedInfo = + DidLinkedInfo; + +fn raw_did_endpoint_to_rpc( + raw: ServiceEndpoint, Vec, Vec>, +) -> Option> { + Some(ServiceEndpoint { + id: String::from_utf8(raw.id).ok()?, + service_types: raw + .service_types + .into_iter() + .filter_map(|st| String::from_utf8(st).ok()) + .collect(), + urls: raw + .urls + .into_iter() + .filter_map(|url| String::from_utf8(url).ok()) + .collect(), + }) +} + +pub type DidRpcResponse = + Option>; + +#[rpc(client, server)] +pub trait DidApi +where + BlockNumber: MaxEncodedLen, + Key: Ord, + Balance: FromStr + Display, +{ + /// Given a web3name this returns: + /// * the DID + /// * public keys stored for the did + /// * the web3name (optional) + /// * associated accounts + /// * service endpoints + #[method(name = "did_queryByWeb3Name")] + fn query_did_by_w3n( + &self, + web3name: String, + at: Option, + ) -> RpcResult>; + + /// Given an account this returns: + /// * the DID + /// * public keys stored for the did + /// * the web3name (optional) + /// * associated accounts + /// * service endpoints + #[method(name = "did_queryByAccount")] + fn query_did_by_account_id( + &self, + account: AccountId, + at: Option, + ) -> RpcResult>; + + /// Given a did this returns: + /// * the DID + /// * public keys stored for the did + /// * the web3name (optional) + /// * associated accounts + /// * service endpoints + #[method(name = "did_query")] + fn query_did( + &self, + account: DidIdentifier, + at: Option, + ) -> RpcResult>; +} + +/// A struct that implements the [`DidRuntimeApi`]. +pub struct DidQuery { + client: Arc, + _marker: std::marker::PhantomData, +} + +impl DidQuery { + /// Create new `DidQuery` with the given reference to the client. + pub fn new(client: Arc) -> Self { + Self { + client, + _marker: Default::default(), + } + } +} + +/// Error type of this RPC api. +pub enum Error { + /// The transaction was not decodable. + DecodeError, + /// The call to runtime failed. + RuntimeError, +} + +impl From for i32 { + fn from(e: Error) -> i32 { + match e { + Error::RuntimeError => 1, + Error::DecodeError => 2, + } + } +} + +#[async_trait] +impl + DidApiServer<::Hash, DidIdentifier, AccountId, Balance, Key, BlockNumber> for DidQuery +where + AccountId: Codec + Send + Sync + 'static, + DidIdentifier: Codec + Send + Sync + 'static, + Key: Codec + Ord, + Balance: Codec + FromStr + Display, + BlockNumber: Codec + MaxEncodedLen, + Block: BlockT, + Client: Send + Sync + 'static + ProvideRuntimeApi + HeaderBackend, + Client::Api: DidRuntimeApi, +{ + fn query_did_by_w3n( + &self, + web3name: String, + at: Option<::Hash>, + ) -> RpcResult>> { + let api = self.client.runtime_api(); + let at = BlockId::hash(at.unwrap_or_else(|| + // If the block hash is not provided, assume the best block. + self.client.info().best_hash)); + + match api.query_did_by_w3n(&at, web3name.into()) { + Err(e) => Err(CallError::Custom(ErrorObject::owned( + Error::RuntimeError.into(), + "Unable to query DID by web3name.", + Some(format!("{:?}", e)), + )) + .into()), + Ok(doc) => Ok(doc.map(|doc| RpcDidLinkedInfo { + // convert the w3n from a byte array to a string. if it's invalid utf-8 which should never happen, we + // ignore the w3n and pretend it doesn't exist. + w3n: doc.w3n.and_then(|w3n| String::from_utf8(w3n).ok()), + accounts: doc.accounts, + identifier: doc.identifier, + service_endpoints: doc + .service_endpoints + .into_iter() + .filter_map(raw_did_endpoint_to_rpc) + .collect(), + details: doc.details, + })), + } + } + + fn query_did_by_account_id( + &self, + account: AccountId, + at: Option<::Hash>, + ) -> RpcResult>> { + let api = self.client.runtime_api(); + let at = BlockId::hash(at.unwrap_or_else(|| + // If the block hash is not supplied assume the best block. + self.client.info().best_hash)); + + match api.query_did_by_account_id(&at, account) { + Err(e) => Err(CallError::Custom(ErrorObject::owned( + Error::RuntimeError.into(), + "Unable to query account by DID.", + Some(format!("{:?}", e)), + )) + .into()), + Ok(doc) => Ok(doc.map(|doc| RpcDidLinkedInfo { + // convert the w3n from a byte array to a string. if it's invalid utf-8 which should never happen, we + // ignore the w3n and pretend it doesn't exist. + w3n: doc.w3n.and_then(|w3n| String::from_utf8(w3n).ok()), + accounts: doc.accounts, + identifier: doc.identifier, + service_endpoints: doc + .service_endpoints + .into_iter() + .filter_map(raw_did_endpoint_to_rpc) + .collect(), + details: doc.details, + })), + } + } + + fn query_did( + &self, + did: DidIdentifier, + at: Option<::Hash>, + ) -> RpcResult>> { + let api = self.client.runtime_api(); + let at = BlockId::hash(at.unwrap_or_else(|| + // If the block hash is not supplied assume the best block. + self.client.info().best_hash)); + + match api.query_did(&at, did) { + Err(e) => Err(CallError::Custom(ErrorObject::owned( + Error::RuntimeError.into(), + "Unable to query DID details.", + Some(format!("{:?}", e)), + )) + .into()), + Ok(doc) => Ok(doc.map(|doc| RpcDidLinkedInfo { + // convert the w3n from a byte array to a string. if it's invalid utf-8 which should never happen, we + // ignore the w3n and pretend it doesn't exist. + w3n: doc.w3n.and_then(|w3n| String::from_utf8(w3n).ok()), + accounts: doc.accounts, + identifier: doc.identifier, + service_endpoints: doc + .service_endpoints + .into_iter() + .filter_map(raw_did_endpoint_to_rpc) + .collect(), + details: doc.details, + })), + } + } +} diff --git a/runtimes/peregrine/Cargo.toml b/runtimes/peregrine/Cargo.toml index 8faeb67bb4..a73971d6e8 100644 --- a/runtimes/peregrine/Cargo.toml +++ b/runtimes/peregrine/Cargo.toml @@ -18,6 +18,7 @@ hex-literal = {version = "0.3.4", optional = true} serde = {version = "1.0.137", optional = true, features = ["derive"]} # RPC +did-rpc-runtime-api = {path = "../../rpc/did/runtime-api", default-features = false} frame-system-rpc-runtime-api = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.24"} pallet-transaction-payment-rpc-runtime-api = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.24"} @@ -27,10 +28,10 @@ ctype = {default-features = false, path = "../../pallets/ctype"} delegation = {default-features = false, path = "../../pallets/delegation"} did = {default-features = false, path = "../../pallets/did"} kilt-support = {path = "../../support", default-features = false, optional = true} -pallet-did-lookup = {default-features = false, path = "../../pallets/pallet-did-lookup"} +pallet-did-lookup = {path = "../../pallets/pallet-did-lookup", default-features = false} pallet-inflation = {path = "../../pallets/pallet-inflation", default-features = false} -pallet-web3-names = {default-features = false, path = "../../pallets/pallet-web3-names"} -parachain-staking = {default-features = false, path = "../../pallets/parachain-staking"} +pallet-web3-names = {path = "../../pallets/pallet-web3-names", default-features = false} +parachain-staking = {path = "../../pallets/parachain-staking", default-features = false} runtime-common = {path = "../../runtimes/common", default-features = false} # Substrate dependencies @@ -141,6 +142,7 @@ std = [ "cumulus-primitives-core/std", "cumulus-primitives-timestamp/std", "delegation/std", + "did-rpc-runtime-api/std", "did/std", "frame-benchmarking/std", "frame-executive/std", diff --git a/runtimes/peregrine/src/lib.rs b/runtimes/peregrine/src/lib.rs index 33e544d9fb..856eace4bd 100644 --- a/runtimes/peregrine/src/lib.rs +++ b/runtimes/peregrine/src/lib.rs @@ -1074,6 +1074,93 @@ impl_runtime_apis! { } } + impl did_rpc_runtime_api::DidApi< + Block, + DidIdentifier, + AccountId, + Balance, + Hash, + BlockNumber + > for Runtime { + fn query_did_by_w3n(name: Vec) -> Option + > { + let name: pallet_web3_names::web3_name::AsciiWeb3Name = name.try_into().ok()?; + pallet_web3_names::Owner::::get(&name) + .and_then(|owner_info| { + did::Did::::get(&owner_info.owner).map(|details| (owner_info, details)) + }) + .map(|(owner_info, details)| { + let accounts = pallet_did_lookup::ConnectedAccounts::::iter_key_prefix(&owner_info.owner).collect(); + let service_endpoints = did::ServiceEndpoints::::iter_prefix(&owner_info.owner).map(|e| From::from(e.1)).collect(); + + did_rpc_runtime_api::RawDidLinkedInfo { + identifier: owner_info.owner, + w3n: Some(name.into()), + accounts, + service_endpoints, + details: details.into(), + } + }) + } + + fn query_did_by_account_id(account: AccountId) -> Option< + did_rpc_runtime_api::RawDidLinkedInfo< + DidIdentifier, + AccountId, + Balance, + Hash, + BlockNumber + > + > { + pallet_did_lookup::ConnectedDids::::get(account) + .and_then(|owner_info| { + did::Did::::get(&owner_info.did).map(|details| (owner_info, details)) + }) + .map(|(connection_record, details)| { + let w3n = pallet_web3_names::Names::::get(&connection_record.did).map(Into::into); + let accounts = pallet_did_lookup::ConnectedAccounts::::iter_key_prefix(&connection_record.did).collect(); + let service_endpoints = did::ServiceEndpoints::::iter_prefix(&connection_record.did).map(|e| From::from(e.1)).collect(); + + did_rpc_runtime_api::RawDidLinkedInfo { + identifier: connection_record.did, + w3n, + accounts, + service_endpoints, + details: details.into(), + } + }) + } + + fn query_did(did: DidIdentifier) -> Option< + did_rpc_runtime_api::RawDidLinkedInfo< + DidIdentifier, + AccountId, + Balance, + Hash, + BlockNumber + > + > { + let details = did::Did::::get(&did)?; + let w3n = pallet_web3_names::Names::::get(&did).map(Into::into); + let accounts = pallet_did_lookup::ConnectedAccounts::::iter_key_prefix(&did).collect(); + let service_endpoints = did::ServiceEndpoints::::iter_prefix(&did).map(|e| From::from(e.1)).collect(); + + Some(did_rpc_runtime_api::RawDidLinkedInfo { + identifier: did, + w3n, + accounts, + service_endpoints, + details: details.into(), + }) + } + } + #[cfg(feature = "runtime-benchmarks")] impl frame_benchmarking::Benchmark for Runtime { fn benchmark_metadata(extra: bool) -> ( diff --git a/runtimes/spiritnet/Cargo.toml b/runtimes/spiritnet/Cargo.toml index cd00edcb71..b294f4ac95 100644 --- a/runtimes/spiritnet/Cargo.toml +++ b/runtimes/spiritnet/Cargo.toml @@ -20,6 +20,7 @@ serde = {version = "1.0.137", optional = true, features = ["derive"]} # RPC frame-system-rpc-runtime-api = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.24"} pallet-transaction-payment-rpc-runtime-api = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.24"} +did-rpc-runtime-api = {path = "../../rpc/did/runtime-api", default-features = false} # KILT pallets & primitives attestation = {default-features = false, path = "../../pallets/attestation"} @@ -142,6 +143,7 @@ std = [ "cumulus-primitives-timestamp/std", "delegation/std", "did/std", + "did-rpc-runtime-api/std", "frame-benchmarking/std", "frame-executive/std", "frame-support/std", diff --git a/runtimes/spiritnet/src/lib.rs b/runtimes/spiritnet/src/lib.rs index 5abad285f7..067ecd9b46 100644 --- a/runtimes/spiritnet/src/lib.rs +++ b/runtimes/spiritnet/src/lib.rs @@ -1067,6 +1067,93 @@ impl_runtime_apis! { } } + impl did_rpc_runtime_api::DidApi< + Block, + DidIdentifier, + AccountId, + Balance, + Hash, + BlockNumber + > for Runtime { + fn query_did_by_w3n(name: Vec) -> Option + > { + let name: pallet_web3_names::web3_name::AsciiWeb3Name = name.try_into().ok()?; + pallet_web3_names::Owner::::get(&name) + .and_then(|owner_info| { + did::Did::::get(&owner_info.owner).map(|details| (owner_info, details)) + }) + .map(|(owner_info, details)| { + let accounts = pallet_did_lookup::ConnectedAccounts::::iter_key_prefix(&owner_info.owner).collect(); + let service_endpoints = did::ServiceEndpoints::::iter_prefix(&owner_info.owner).map(|e| From::from(e.1)).collect(); + + did_rpc_runtime_api::RawDidLinkedInfo { + identifier: owner_info.owner, + w3n: Some(name.into()), + accounts, + service_endpoints, + details: details.into(), + } + }) + } + + fn query_did_by_account_id(account: AccountId) -> Option< + did_rpc_runtime_api::RawDidLinkedInfo< + DidIdentifier, + AccountId, + Balance, + Hash, + BlockNumber + > + > { + pallet_did_lookup::ConnectedDids::::get(account) + .and_then(|owner_info| { + did::Did::::get(&owner_info.did).map(|details| (owner_info, details)) + }) + .map(|(connection_record, details)| { + let w3n = pallet_web3_names::Names::::get(&connection_record.did).map(Into::into); + let accounts = pallet_did_lookup::ConnectedAccounts::::iter_key_prefix(&connection_record.did).collect(); + let service_endpoints = did::ServiceEndpoints::::iter_prefix(&connection_record.did).map(|e| From::from(e.1)).collect(); + + did_rpc_runtime_api::RawDidLinkedInfo { + identifier: connection_record.did, + w3n, + accounts, + service_endpoints, + details: details.into(), + } + }) + } + + fn query_did(did: DidIdentifier) -> Option< + did_rpc_runtime_api::RawDidLinkedInfo< + DidIdentifier, + AccountId, + Balance, + Hash, + BlockNumber + > + > { + let details = did::Did::::get(&did)?; + let w3n = pallet_web3_names::Names::::get(&did).map(Into::into); + let accounts = pallet_did_lookup::ConnectedAccounts::::iter_key_prefix(&did).collect(); + let service_endpoints = did::ServiceEndpoints::::iter_prefix(&did).map(|e| From::from(e.1)).collect(); + + Some(did_rpc_runtime_api::RawDidLinkedInfo { + identifier: did, + w3n, + accounts, + service_endpoints, + details: details.into(), + }) + } + } + #[cfg(feature = "runtime-benchmarks")] impl frame_benchmarking::Benchmark for Runtime { fn benchmark_metadata(extra: bool) -> ( diff --git a/runtimes/standalone/Cargo.toml b/runtimes/standalone/Cargo.toml index e102f2cb96..0f972b2c70 100644 --- a/runtimes/standalone/Cargo.toml +++ b/runtimes/standalone/Cargo.toml @@ -26,7 +26,10 @@ pallet-did-lookup = {default-features = false, path = "../../pallets/pallet-did- pallet-web3-names = {default-features = false, path = "../../pallets/pallet-web3-names"} runtime-common = {path = "../../runtimes/common", default-features = false} -# kilt specific +# RPC +did-rpc-runtime-api = {path = "../../rpc/did/runtime-api", default-features = false} +frame-system-rpc-runtime-api = {branch = "polkadot-v0.9.24", default-features = false, git = "https://github.com/paritytech/substrate"} +pallet-transaction-payment-rpc-runtime-api = {branch = "polkadot-v0.9.24", default-features = false, git = "https://github.com/paritytech/substrate"} # Benchmarking frame-system-benchmarking = {git = "https://github.com/paritytech/substrate", default-features = false, optional = true, branch = "polkadot-v0.9.24"} @@ -36,7 +39,6 @@ frame-benchmarking = {branch = "polkadot-v0.9.24", default-features = false, git frame-executive = {branch = "polkadot-v0.9.24", default-features = false, git = "https://github.com/paritytech/substrate"} frame-support = {branch = "polkadot-v0.9.24", default-features = false, git = "https://github.com/paritytech/substrate"} frame-system = {branch = "polkadot-v0.9.24", default-features = false, git = "https://github.com/paritytech/substrate"} -frame-system-rpc-runtime-api = {branch = "polkadot-v0.9.24", default-features = false, git = "https://github.com/paritytech/substrate"} pallet-aura = {branch = "polkadot-v0.9.24", default-features = false, git = "https://github.com/paritytech/substrate"} pallet-authorship = {branch = "polkadot-v0.9.24", default-features = false, git = "https://github.com/paritytech/substrate"} pallet-balances = {branch = "polkadot-v0.9.24", default-features = false, git = "https://github.com/paritytech/substrate"} @@ -48,7 +50,6 @@ pallet-session = {branch = "polkadot-v0.9.24", default-features = false, git = " pallet-sudo = {branch = "polkadot-v0.9.24", default-features = false, git = "https://github.com/paritytech/substrate"} pallet-timestamp = {branch = "polkadot-v0.9.24", default-features = false, git = "https://github.com/paritytech/substrate"} pallet-transaction-payment = {branch = "polkadot-v0.9.24", default-features = false, git = "https://github.com/paritytech/substrate"} -pallet-transaction-payment-rpc-runtime-api = {branch = "polkadot-v0.9.24", default-features = false, git = "https://github.com/paritytech/substrate"} pallet-utility = {branch = "polkadot-v0.9.24", default-features = false, git = "https://github.com/paritytech/substrate"} pallet-vesting = {branch = "polkadot-v0.9.24", default-features = false, git = "https://github.com/paritytech/substrate"} sp-api = {branch = "polkadot-v0.9.24", default-features = false, git = "https://github.com/paritytech/substrate"} @@ -103,6 +104,7 @@ std = [ "frame-support/std", "frame-system/std", "frame-system-rpc-runtime-api/std", + "did-rpc-runtime-api/std", "frame-try-runtime/std", "log/std", "pallet-aura/std", diff --git a/runtimes/standalone/src/lib.rs b/runtimes/standalone/src/lib.rs index 6ddfab5a7c..5f001739d1 100644 --- a/runtimes/standalone/src/lib.rs +++ b/runtimes/standalone/src/lib.rs @@ -28,17 +28,14 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); use codec::{Decode, Encode, MaxEncodedLen}; -use delegation::DelegationAc; -pub use frame_support::{ +use frame_support::{ construct_runtime, parameter_types, - traits::{Currency, FindAuthor, Imbalance, KeyOwnerProofSystem, OnUnbalanced, Randomness}, + traits::{Currency, InstanceFilter, KeyOwnerProofSystem}, weights::{ - constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND}, - IdentityFee, Weight, + constants::{RocksDbWeight, WEIGHT_PER_SECOND}, + ConstantMultiplier, IdentityFee, }, - ConsensusEngineId, StorageValue, }; -use frame_support::{traits::InstanceFilter, weights::ConstantMultiplier}; pub use frame_system::Call as SystemCall; use frame_system::EnsureRoot; use pallet_grandpa::{fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList}; @@ -52,11 +49,19 @@ use sp_runtime::{ transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, RuntimeDebug, }; -pub use sp_runtime::{Perbill, Permill}; use sp_std::prelude::*; use sp_version::RuntimeVersion; +use delegation::DelegationAc; +use runtime_common::{ + authorization::{AuthorizationId, PalletAuthorize}, + constants::{self, KILT, MILLI_KILT}, + fees::ToAuthor, + AccountId, Balance, BlockNumber, DidIdentifier, Hash, Index, Signature, SlowAdjustingFeeUpdate, +}; + pub use pallet_timestamp::Call as TimestampCall; +pub use sp_runtime::{Perbill, Permill}; pub use attestation; pub use ctype; @@ -64,12 +69,6 @@ pub use delegation; pub use did; pub use pallet_balances::Call as BalancesCall; pub use pallet_web3_names; -use runtime_common::{ - authorization::{AuthorizationId, PalletAuthorize}, - constants::{self, KILT, MILLI_KILT}, - fees::ToAuthor, - AccountId, Balance, BlockNumber, DidIdentifier, Hash, Index, Signature, SlowAdjustingFeeUpdate, -}; #[cfg(feature = "std")] use sp_version::NativeVersion; @@ -414,10 +413,6 @@ impl did::Config for Runtime { type WeightInfo = (); } -parameter_types! { - pub const DidLookupDeposit: Balance = constants::did_lookup::DID_CONNECTION_DEPOSIT; -} - impl pallet_did_lookup::Config for Runtime { type Event = Event; type Signature = Signature; @@ -425,7 +420,7 @@ impl pallet_did_lookup::Config for Runtime { type DidIdentifier = DidIdentifier; type Currency = Balances; - type Deposit = DidLookupDeposit; + type Deposit = constants::did_lookup::DidLookupDeposit; type EnsureOrigin = did::EnsureDidOrigin; type OriginSuccess = did::DidRawOrigin; @@ -433,21 +428,15 @@ impl pallet_did_lookup::Config for Runtime { type WeightInfo = (); } -parameter_types! { - pub const Web3NameDeposit: Balance = constants::web3_names::DEPOSIT; - pub const MinNameLength: u32 = constants::web3_names::MIN_LENGTH; - pub const MaxNameLength: u32 = constants::web3_names::MAX_LENGTH; -} - impl pallet_web3_names::Config for Runtime { type BanOrigin = EnsureRoot; type OwnerOrigin = did::EnsureDidOrigin; type OriginSuccess = did::DidRawOrigin; type Currency = Balances; - type Deposit = Web3NameDeposit; + type Deposit = constants::web3_names::Web3NameDeposit; type Event = Event; - type MaxNameLength = MaxNameLength; - type MinNameLength = MinNameLength; + type MaxNameLength = constants::web3_names::MaxNameLength; + type MinNameLength = constants::web3_names::MinNameLength; type Web3Name = pallet_web3_names::web3_name::AsciiWeb3Name; type Web3NameOwner = DidIdentifier; type WeightInfo = (); @@ -892,6 +881,93 @@ impl_runtime_apis! { } } + impl did_rpc_runtime_api::DidApi< + Block, + DidIdentifier, + AccountId, + Balance, + Hash, + BlockNumber + > for Runtime { + fn query_did_by_w3n(name: Vec) -> Option + > { + let name: pallet_web3_names::web3_name::AsciiWeb3Name = name.try_into().ok()?; + pallet_web3_names::Owner::::get(&name) + .and_then(|owner_info| { + did::Did::::get(&owner_info.owner).map(|details| (owner_info, details)) + }) + .map(|(owner_info, details)| { + let accounts = pallet_did_lookup::ConnectedAccounts::::iter_key_prefix(&owner_info.owner).collect(); + let service_endpoints = did::ServiceEndpoints::::iter_prefix(&owner_info.owner).map(|e| From::from(e.1)).collect(); + + did_rpc_runtime_api::RawDidLinkedInfo { + identifier: owner_info.owner, + w3n: Some(name.into()), + accounts, + service_endpoints, + details: details.into(), + } + }) + } + + fn query_did_by_account_id(account: AccountId) -> Option< + did_rpc_runtime_api::RawDidLinkedInfo< + DidIdentifier, + AccountId, + Balance, + Hash, + BlockNumber + > + > { + pallet_did_lookup::ConnectedDids::::get(account) + .and_then(|owner_info| { + did::Did::::get(&owner_info.did).map(|details| (owner_info, details)) + }) + .map(|(connection_record, details)| { + let w3n = pallet_web3_names::Names::::get(&connection_record.did).map(Into::into); + let accounts = pallet_did_lookup::ConnectedAccounts::::iter_key_prefix(&connection_record.did).collect(); + let service_endpoints = did::ServiceEndpoints::::iter_prefix(&connection_record.did).map(|e| From::from(e.1)).collect(); + + did_rpc_runtime_api::RawDidLinkedInfo { + identifier: connection_record.did, + w3n, + accounts, + service_endpoints, + details: details.into(), + } + }) + } + + fn query_did(did: DidIdentifier) -> Option< + did_rpc_runtime_api::RawDidLinkedInfo< + DidIdentifier, + AccountId, + Balance, + Hash, + BlockNumber + > + > { + let details = did::Did::::get(&did)?; + let w3n = pallet_web3_names::Names::::get(&did).map(Into::into); + let accounts = pallet_did_lookup::ConnectedAccounts::::iter_key_prefix(&did).collect(); + let service_endpoints = did::ServiceEndpoints::::iter_prefix(&did).map(|e| From::from(e.1)).collect(); + + Some(did_rpc_runtime_api::RawDidLinkedInfo { + identifier: did, + w3n, + accounts, + service_endpoints, + details: details.into(), + }) + } + } + #[cfg(feature = "runtime-benchmarks")] impl frame_benchmarking::Benchmark for Runtime { fn benchmark_metadata(extra: bool) -> ( @@ -975,7 +1051,7 @@ impl_runtime_apis! { #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime for Runtime { - fn on_runtime_upgrade() -> (Weight, Weight) { + fn on_runtime_upgrade() -> (frame_support::pallet_prelude::Weight, frame_support::pallet_prelude::Weight) { log::info!("try-runtime::on_runtime_upgrade standalone runtime."); let weight = Executive::try_runtime_upgrade().map_err(|err|{ log::info!("try-runtime::on_runtime_upgrade failed with: {:?}", err); @@ -983,7 +1059,7 @@ impl_runtime_apis! { }).unwrap(); (weight, BlockWeights::get().max_block) } - fn execute_block_no_check(block: Block) -> Weight { + fn execute_block_no_check(block: Block) -> frame_support::pallet_prelude::Weight { Executive::execute_block_no_check(block) } } diff --git a/scripts/all.fish b/scripts/all.fish index 57693e947d..7acfbed744 100644 --- a/scripts/all.fish +++ b/scripts/all.fish @@ -2,14 +2,30 @@ # Check a few feature combinations for all crates. # Requires `cargo-workspaces` to be installed. +echo "Features check started..." + +for features in "--features default" "--all-features" "--features runtime-benchmarks" "--features try-runtime" + for package in (cargo workspaces list) + cargo check -p $package (echo $features | string split " ") > /dev/null ^ /dev/null + if [ "$status" = "0" ] + echo -n "[ok] " + else + echo -n "[fail] " + end + echo cargo check -p $package (echo $features | string split " ") + end +end + for features in "--features default" "--all-features" "--features runtime-benchmarks" "--features try-runtime" for package in (cargo workspaces list) - cargo build -p $package (echo $features | string split " ") > /dev/null ^ /dev/null + cargo test -p $package (echo $features | string split " ") > /dev/null ^ /dev/null if [ "$status" = "0" ] echo -n "[ok] " else echo -n "[fail] " end - echo cargo build -p $package (echo $features | string split " ") + echo cargo test -p $package (echo $features | string split " ") end end + +echo "Features check completed!" diff --git a/support/Cargo.toml b/support/Cargo.toml index eaca55f0c8..3f187516fa 100644 --- a/support/Cargo.toml +++ b/support/Cargo.toml @@ -9,6 +9,7 @@ version = "1.7.0" [dependencies] codec = {package = "parity-scale-codec", version = "3.1.2", default-features = false, features = ["derive"]} scale-info = {version = "2.1.1", default-features = false, features = ["derive"]} +serde = {version = "1.0.136", optional = true, features = ["derive"]} frame-support = {branch = "polkadot-v0.9.24", default-features = false, git = "https://github.com/paritytech/substrate"} frame-system = {branch = "polkadot-v0.9.24", default-features = false, git = "https://github.com/paritytech/substrate"} @@ -16,6 +17,9 @@ sp-core = {branch = "polkadot-v0.9.24", default-features = false, git = "https:/ sp-runtime = {branch = "polkadot-v0.9.24", default-features = false, git = "https://github.com/paritytech/substrate"} sp-std = {branch = "polkadot-v0.9.24", default-features = false, git = "https://github.com/paritytech/substrate"} +[dev-dependencies] +serde_json = "1.0.79" + [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -28,6 +32,7 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", ] std = [ + "serde", "codec/std", "frame-support/std", "frame-system/std", diff --git a/support/src/deposit.rs b/support/src/deposit.rs index a36a96e203..836898ab76 100644 --- a/support/src/deposit.rs +++ b/support/src/deposit.rs @@ -15,12 +15,82 @@ // along with this program. If not, see . // If you feel like getting in touch with us, you can do so at info@botlabs.org + use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; /// An amount of balance reserved by the specified address. #[derive(Clone, Debug, Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[cfg_attr( + feature = "std", + serde(bound( + serialize = " + Balance: std::fmt::Display, + Account: Serialize", + deserialize = " + Balance: std::str::FromStr, + Account: Deserialize<'de>" + )) +)] pub struct Deposit { pub owner: Account, + #[cfg_attr(feature = "std", serde(with = "serde_balance"))] pub amount: Balance, } + +// This code was copied from https://github.com/paritytech/substrate/blob/ded44948e2d5a398abcb4e342b0513cb690961bb/frame/transaction-payment/src/types.rs#L113 +// It is needed because u128 cannot be serialized by serde out of the box. +#[cfg(feature = "std")] +mod serde_balance { + use serde::{Deserialize, Deserializer, Serializer}; + + pub fn serialize(t: &T, serializer: S) -> Result { + serializer.serialize_str(&t.to_string()) + } + + pub fn deserialize<'de, D: Deserializer<'de>, T: std::str::FromStr>(deserializer: D) -> Result { + let s = String::deserialize(deserializer)?; + s.parse::() + .map_err(|_| serde::de::Error::custom("Parse from string failed")) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_serialize_and_deserialize_properly_with_string() { + let deposit = Deposit { + owner: 0_u8, + amount: 1_000_000_u128, + }; + + let json_str = r#"{"owner":0,"amount":"1000000"}"#; + + assert_eq!(serde_json::to_string(&deposit).unwrap(), json_str); + assert_eq!(serde_json::from_str::>(json_str).unwrap(), deposit); + + // should not panic + serde_json::to_value(&deposit).unwrap(); + } + + #[test] + fn should_serialize_and_deserialize_properly_large_value() { + let deposit = Deposit { + owner: 0_u8, + amount: 1_000_000_u128, + }; + + let json_str = r#"{"owner":0,"amount":"1000000"}"#; + + assert_eq!(serde_json::to_string(&deposit).unwrap(), json_str); + assert_eq!(serde_json::from_str::>(json_str).unwrap(), deposit); + + // should not panic + serde_json::to_value(&deposit).unwrap(); + } +}