diff --git a/Cargo.lock b/Cargo.lock index 96fe117ef3..258fca0c15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2381,14 +2381,11 @@ dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", - "cumulus-pallet-xcm", - "cumulus-pallet-xcmp-queue", "cumulus-primitives-core", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "did", "dip-provider-runtime-template", - "dip-support", "frame-executive", "frame-support", "frame-system", @@ -2400,13 +2397,14 @@ dependencies = [ "pallet-collator-selection", "pallet-did-lookup", "pallet-dip-consumer", + "pallet-postit", + "pallet-relay-store", "pallet-session", "pallet-sudo", "pallet-timestamp", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-utility", - "pallet-xcm", "parachain-info", "parity-scale-codec", "polkadot-parachain", @@ -2424,9 +2422,6 @@ dependencies = [ "sp-transaction-pool", "sp-version", "substrate-wasm-builder", - "xcm", - "xcm-builder", - "xcm-executor", ] [[package]] @@ -2486,13 +2481,10 @@ dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", - "cumulus-pallet-xcm", - "cumulus-pallet-xcmp-queue", "cumulus-primitives-core", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "did", - "dip-support", "frame-executive", "frame-support", "frame-system", @@ -2511,7 +2503,6 @@ dependencies = [ "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-web3-names", - "pallet-xcm", "parachain-info", "parity-scale-codec", "runtime-common", @@ -2528,18 +2519,6 @@ dependencies = [ "sp-transaction-pool", "sp-version", "substrate-wasm-builder", - "xcm", - "xcm-builder", - "xcm-executor", -] - -[[package]] -name = "dip-support" -version = "1.12.0-dev" -dependencies = [ - "frame-support", - "parity-scale-codec", - "scale-info", ] [[package]] @@ -4402,16 +4381,22 @@ dependencies = [ name = "kilt-dip-support" version = "1.12.0-dev" dependencies = [ + "cumulus-pallet-parachain-system", "did", - "dip-support", "frame-support", "frame-system", + "hash-db", + "hex-literal 0.3.4", "pallet-dip-consumer", "pallet-dip-provider", + "pallet-relay-store", "parity-scale-codec", + "rococo-runtime", "scale-info", "sp-core", + "sp-io", "sp-runtime", + "sp-state-machine", "sp-std", "sp-trie", "xcm", @@ -6429,13 +6414,12 @@ dependencies = [ name = "pallet-dip-consumer" version = "1.12.0-dev" dependencies = [ - "cumulus-pallet-xcm", - "dip-support", "frame-support", "frame-system", "kilt-support", "parity-scale-codec", "scale-info", + "sp-core", "sp-std", ] @@ -6444,13 +6428,11 @@ name = "pallet-dip-provider" version = "1.12.0-dev" dependencies = [ "did", - "dip-support", "frame-support", "frame-system", "parity-scale-codec", "scale-info", "sp-std", - "xcm", ] [[package]] @@ -6791,6 +6773,18 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-postit" +version = "1.12.0-dev" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-preimage" version = "4.0.0-dev" @@ -6875,6 +6869,21 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-relay-store" +version = "1.12.0-dev" +dependencies = [ + "cumulus-pallet-parachain-system", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-scheduler" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index 26068c6f9f..cf103d157b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ version = "1.12.0-dev" members = [ "crates/*", "dip-template/nodes/*", + "dip-template/pallets/*", "dip-template/runtimes/*", "nodes/*", "pallets/*", @@ -35,13 +36,14 @@ clap = "4.1.6" env_logger = "0.10.0" fluent-uri = { version = "0.1.4", default-features = false } futures = {version = "0.3.21", default-features = false} +hash-db = { version = "0.16.0", default-features = false } hex = {version = "0.4.0", default-features = false} hex-literal = "0.3.4" jsonrpsee = "0.16.2" libsecp256k1 = {version = "0.7", default-features = false} log = "0.4.17" parity-scale-codec = {version = "3.1.5", default-features = false} -scale-info = {version = "2.1.1", default-features = false} +scale-info = {version = "2.7.0", default-features = false} serde = "1.0.144" serde_json = "1.0.85" sha3 = {version = "0.10.0", default-features = false} @@ -57,12 +59,12 @@ pallet-dip-consumer = {path = "pallets/pallet-dip-consumer", default-features = pallet-dip-provider = {path = "pallets/pallet-dip-provider", default-features = false} pallet-did-lookup = {path = "pallets/pallet-did-lookup", default-features = false} pallet-inflation = {path = "pallets/pallet-inflation", default-features = false} +pallet-relay-store = {path = "pallets/pallet-relay-store", default-features = false} pallet-web3-names = {path = "pallets/pallet-web3-names", default-features = false} parachain-staking = {path = "pallets/parachain-staking", default-features = false} public-credentials = {path = "pallets/public-credentials", default-features = false} # Internal support (with default disabled) -dip-support = {path = "crates/dip-support", default-features = false} kilt-asset-dids = {path = "crates/assets", default-features = false} kilt-dip-support = {path = "crates/kilt-dip-support", default-features = false} kilt-support = {path = "support", default-features = false} @@ -71,6 +73,7 @@ runtime-common = {path = "runtimes/common", default-features = false} # Templates dip-consumer-runtime-template = {path = "dip-template/runtimes/dip-consumer", default-features = false} dip-provider-runtime-template = {path = "dip-template/runtimes/dip-provider", default-features = false} +pallet-postit = {path = "dip-template/pallets/pallet-postit", default-features = false} # Internal runtime API (with default disabled) kilt-runtime-api-did = {path = "runtime-api/did", default-features = false} @@ -138,6 +141,7 @@ sp-offchain = {git = "https://github.com/paritytech/substrate", default-features sp-runtime = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43"} sp-session = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43"} sp-staking = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43"} +sp-state-machine = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43"} sp-std = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43"} sp-transaction-pool = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43"} sp-trie = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43"} diff --git a/crates/kilt-dip-support/Cargo.toml b/crates/kilt-dip-support/Cargo.toml index 61b9c8aaeb..49893b6fd6 100644 --- a/crates/kilt-dip-support/Cargo.toml +++ b/crates/kilt-dip-support/Cargo.toml @@ -11,11 +11,14 @@ repository.workspace = true version.workspace = true [dependencies] +# External dependencies +hash-db.workspace = true + # Internal dependencies did.workspace = true -dip-support.workspace = true pallet-dip-consumer.workspace = true pallet-dip-provider.workspace = true +pallet-relay-store.workspace = true # Parity dependencies parity-scale-codec = {workspace = true, features = ["derive"]} @@ -26,31 +29,47 @@ frame-system.workspace = true frame-support.workspace = true sp-runtime.workspace = true sp-core.workspace = true -sp-trie.workspace = true +sp-io.workspace = true +sp-state-machine.workspace = true sp-std.workspace = true +sp-trie.workspace = true # Polkadot dependencies +rococo-runtime.workspace = true xcm.workspace = true xcm-executor.workspace = true +# Cumulus dependencies +cumulus-pallet-parachain-system.workspace = true + +[dev-dependencies] +hex-literal.workspace = true +sp-io = { workspace = true, features = ["std"] } + [features] default = ["std"] std = [ + "hash-db/std", "did/std", - "dip-support/std", "pallet-dip-consumer/std", "pallet-dip-provider/std", + "pallet-relay-store/std", "parity-scale-codec/std", "scale-info/std", "frame-system/std", "frame-support/std", "sp-runtime/std", "sp-core/std", - "sp-trie/std", + "sp-io/std", + "sp-state-machine/std", "sp-std/std", + "sp-trie/std", + "rococo-runtime/std", + "xcm/std", "xcm-executor/std", - "xcm/std" + "cumulus-pallet-parachain-system/std", ] runtime-benchmarks = [ - "pallet-dip-consumer/runtime-benchmarks" + "pallet-dip-consumer/runtime-benchmarks", + "rococo-runtime/runtime-benchmarks" ] diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index c1d37be9a4..7d17b2a8e9 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -21,308 +21,126 @@ use did::{ DidSignature, DidVerificationKeyRelationship, }; use frame_support::ensure; -use pallet_dip_consumer::{identity::IdentityDetails, traits::IdentityProofVerifier}; -use pallet_dip_provider::traits::IdentityProvider; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; -use sp_core::{ConstU64, Get, RuntimeDebug}; +use sp_core::RuntimeDebug; use sp_runtime::traits::CheckedSub; use sp_std::marker::PhantomData; use crate::{ merkle::RevealedDidKey, - traits::{Bump, DidDipOriginFilter}, + traits::{Bump, DidSignatureVerifierContext, DipCallOriginFilter}, }; #[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)] -pub struct TimeBoundDidSignature { - pub signature: DidSignature, - pub block_number: BlockNumber, +pub(crate) struct RevealedDidKeysAndSignature { + pub merkle_leaves: RevealedDidKeys, + pub did_signature: TimeBoundDidSignature, } #[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)] -pub struct MerkleLeavesAndDidSignature { - pub merkle_leaves: MerkleLeaves, - pub did_signature: TimeBoundDidSignature, +pub struct TimeBoundDidSignature { + pub signature: DidSignature, + pub block_number: BlockNumber, } -/// A type that verifies a DID signature over some DID keys revealed by a -/// previously-verified Merkle proof. It requires the `Details` type to -/// implement the `Bump` trait to avoid replay attacks. The basic verification -/// logic verifies that the signature has been generated over the encoded tuple -/// (call, identity details, submitter_address, submission_block_number, -/// genesis_hash). Additional details can be added to the end of the tuple by -/// providing a `SignedExtraProvider`. -pub struct MerkleRevealedDidSignatureVerifier< - KeyId, - BlockNumber, - Digest, - Details, - AccountId, +pub(crate) struct RevealedDidKeysSignatureAndCallVerifier< + Call, + Submitter, + DidLocalDetails, MerkleProofEntries, - BlockNumberProvider, - const SIGNATURE_VALIDITY: u64, - GenesisHashProvider, - Hash, - SignedExtraProvider = (), - SignedExtra = (), + ContextProvider, + RemoteKeyId, + RemoteBlockNumber, + CallVerifier, >( #[allow(clippy::type_complexity)] PhantomData<( - KeyId, - BlockNumber, - Digest, - Details, - AccountId, + Call, + Submitter, + DidLocalDetails, MerkleProofEntries, - BlockNumberProvider, - ConstU64, - GenesisHashProvider, - Hash, - SignedExtraProvider, - SignedExtra, + ContextProvider, + RemoteKeyId, + RemoteBlockNumber, + CallVerifier, )>, ); impl< Call, - Subject, - KeyId, - BlockNumber, - Digest, - Details, - AccountId, + Submitter, + DidLocalDetails, MerkleProofEntries, - BlockNumberProvider, - const SIGNATURE_VALIDITY: u64, - GenesisHashProvider, - Hash, - SignedExtraProvider, - SignedExtra, - > IdentityProofVerifier - for MerkleRevealedDidSignatureVerifier< - KeyId, - BlockNumber, - Digest, - Details, - AccountId, + ContextProvider, + RemoteKeyId, + RemoteBlockNumber, + CallVerifier, + > + RevealedDidKeysSignatureAndCallVerifier< + Call, + Submitter, + DidLocalDetails, MerkleProofEntries, - BlockNumberProvider, - SIGNATURE_VALIDITY, - GenesisHashProvider, - Hash, - SignedExtraProvider, - SignedExtra, + ContextProvider, + RemoteKeyId, + RemoteBlockNumber, + CallVerifier, > where - AccountId: Encode, - BlockNumber: Encode + CheckedSub + Into + PartialOrd + sp_std::fmt::Debug, Call: Encode, - Digest: Encode, - Details: Bump + Encode, - MerkleProofEntries: AsRef<[RevealedDidKey]>, - BlockNumberProvider: Get, - GenesisHashProvider: Get, - Hash: Encode, - SignedExtraProvider: Get, - SignedExtra: Encode, + Submitter: Encode, + ContextProvider: DidSignatureVerifierContext, + ContextProvider::BlockNumber: Encode + CheckedSub + From + PartialOrd, + ContextProvider::Hash: Encode, + ContextProvider::SignedExtra: Encode, + DidLocalDetails: Bump + Default + Encode, + MerkleProofEntries: sp_std::borrow::Borrow<[RevealedDidKey]>, + CallVerifier: DipCallOriginFilter, { - // TODO: Error handling - type Error = (); - /// The proof must be a list of Merkle leaves that have been previously - /// verified by the Merkle proof verifier, and the additional DID signature. - type Proof = MerkleLeavesAndDidSignature; - /// The `Details` that are part of the identity details must implement the - /// `Bump` trait. - type IdentityDetails = IdentityDetails; - /// The type of the submitter's accounts. - type Submitter = AccountId; - /// Successful verifications return the verification key used to validate - /// the provided signature and its relationship to the DID subject. - type VerificationResult = (DidVerificationKey, DidVerificationKeyRelationship); - - fn verify_proof_for_call_against_details( + #[allow(clippy::result_unit_err)] + pub(crate) fn verify_did_signature_for_call( call: &Call, - _subject: &Subject, - submitter: &Self::Submitter, - identity_details: &mut Self::IdentityDetails, - proof: &Self::Proof, - ) -> Result { - let block_number = BlockNumberProvider::get(); - let is_signature_fresh = - if let Some(blocks_ago_from_now) = block_number.checked_sub(&proof.did_signature.block_number) { - // False if the signature is too old. - blocks_ago_from_now.into() <= SIGNATURE_VALIDITY - } else { - // Signature generated at a future time, not possible to verify. - false - }; - + submitter: &Submitter, + local_details: &mut Option, + merkle_revealed_did_signature: RevealedDidKeysAndSignature, + ) -> Result<(DidVerificationKey, DidVerificationKeyRelationship), ()> { + let block_number = ContextProvider::block_number(); + let is_signature_fresh = if let Some(blocks_ago_from_now) = + block_number.checked_sub(&merkle_revealed_did_signature.did_signature.block_number) + { + // False if the signature is too old. + blocks_ago_from_now <= ContextProvider::SIGNATURE_VALIDITY.into() + } else { + // Signature generated at a future time, not possible to verify. + false + }; ensure!(is_signature_fresh, ()); let encoded_payload = ( call, - &identity_details.details, + &local_details, submitter, - &proof.did_signature.block_number, - GenesisHashProvider::get(), - SignedExtraProvider::get(), + &merkle_revealed_did_signature.did_signature.block_number, + ContextProvider::genesis_hash(), + ContextProvider::signed_extra(), ) .encode(); // Only consider verification keys from the set of revealed keys. - let mut proof_verification_keys = proof.merkle_leaves.as_ref().iter().filter_map(|RevealedDidKey { relationship, details: DidPublicKeyDetails { key, .. }, .. } | { - let DidPublicKey::PublicVerificationKey(key) = key else { return None }; - Some((key, DidVerificationKeyRelationship::try_from(*relationship).expect("Should never fail to build a VerificationRelationship from the given DidKeyRelationship because we have already made sure the conditions hold."))) - }); + let mut proof_verification_keys = merkle_revealed_did_signature.merkle_leaves.borrow().iter().filter_map(|RevealedDidKey { + relationship, details: DidPublicKeyDetails { key, .. }, .. } | { + let DidPublicKey::PublicVerificationKey(key) = key else { return None }; + Some((key, DidVerificationKeyRelationship::try_from(*relationship).expect("Should never fail to build a VerificationRelationship from the given DidKeyRelationship because we have already made sure the conditions hold."))) }); let valid_signing_key = proof_verification_keys.find(|(verification_key, _)| { verification_key - .verify_signature(&encoded_payload, &proof.did_signature.signature) + .verify_signature(&encoded_payload, &merkle_revealed_did_signature.did_signature.signature) .is_ok() }); let Some((key, relationship)) = valid_signing_key else { return Err(()) }; - identity_details.details.bump(); + if let Some(details) = local_details { + details.bump(); + } else { + *local_details = Some(DidLocalDetails::default()); + }; + CallVerifier::check_call_origin_info(call, &(key.clone(), relationship)).map_err(|_| ())?; Ok((key.clone(), relationship)) } } - -/// A type that chains a DID signature verification, as provided by -/// `MerkleRevealedDidSignatureVerifier`, and a call filtering logic based on -/// the type of key used in the signature. -/// Verification bails out early in case of invalid DID signatures. Otherwise, -/// the retrieved key and its relationship is passed to the call verifier to do -/// some additional lookups on the call. -/// The `CallVerifier` only performs internal checks, while all input and output -/// types are taken from the provided `DidSignatureVerifier` type. -pub struct DidSignatureAndCallVerifier( - PhantomData<(DidSignatureVerifier, CallVerifier)>, -); - -impl IdentityProofVerifier - for DidSignatureAndCallVerifier -where - DidSignatureVerifier: IdentityProofVerifier, - CallVerifier: DidDipOriginFilter, -{ - // FIXME: Better error handling - type Error = (); - /// The input proof is the same accepted by the `DidSignatureVerifier`. - type Proof = DidSignatureVerifier::Proof; - /// The identity details are the same accepted by the - /// `DidSignatureVerifier`. - type IdentityDetails = DidSignatureVerifier::IdentityDetails; - /// The submitter address is the same accepted by the - /// `DidSignatureVerifier`. - type Submitter = DidSignatureVerifier::Submitter; - /// The verification result is the same accepted by the - /// `DidSignatureVerifier`. - type VerificationResult = DidSignatureVerifier::VerificationResult; - - fn verify_proof_for_call_against_details( - call: &Call, - subject: &Subject, - submitter: &Self::Submitter, - identity_details: &mut Self::IdentityDetails, - proof: &Self::Proof, - ) -> Result { - let did_signing_key = DidSignatureVerifier::verify_proof_for_call_against_details( - call, - subject, - submitter, - identity_details, - proof, - ) - .map_err(|_| ())?; - CallVerifier::check_call_origin_info(call, &did_signing_key).map_err(|_| ())?; - Ok(did_signing_key) - } -} - -pub struct CombinedIdentityResult { - pub a: OutputA, - pub b: OutputB, - pub c: OutputC, -} - -impl From<(OutputA, OutputB, OutputC)> - for CombinedIdentityResult -{ - fn from(value: (OutputA, OutputB, OutputC)) -> Self { - Self { - a: value.0, - b: value.1, - c: value.2, - } - } -} - -impl CombinedIdentityResult -where - OutputB: Default, - OutputC: Default, -{ - pub fn from_a(a: OutputA) -> Self { - Self { - a, - b: OutputB::default(), - c: OutputC::default(), - } - } -} - -impl CombinedIdentityResult -where - OutputA: Default, - OutputC: Default, -{ - pub fn from_b(b: OutputB) -> Self { - Self { - a: OutputA::default(), - b, - c: OutputC::default(), - } - } -} - -impl CombinedIdentityResult -where - OutputA: Default, - OutputB: Default, -{ - pub fn from_c(c: OutputC) -> Self { - Self { - a: OutputA::default(), - b: OutputB::default(), - c, - } - } -} - -pub struct CombineIdentityFrom(PhantomData<(A, B, C)>); - -impl IdentityProvider for CombineIdentityFrom -where - A: IdentityProvider, - B: IdentityProvider, - C: IdentityProvider, -{ - // TODO: Proper error handling - type Error = (); - type Success = CombinedIdentityResult, Option, Option>; - - fn retrieve(identifier: &Identifier) -> Result, Self::Error> { - match ( - A::retrieve(identifier), - B::retrieve(identifier), - C::retrieve(identifier), - ) { - // If no details is returned, return None for the whole result - (Ok(None), Ok(None), Ok(None)) => Ok(None), - // Otherwise, return `Some` or `None` depending on each result - (Ok(ok_a), Ok(ok_b), Ok(ok_c)) => Ok(Some(CombinedIdentityResult { - a: ok_a, - b: ok_b, - c: ok_c, - })), - // If any of them returns an `Err`, return an `Err` - _ => Err(()), - } - } -} diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-support/src/lib.rs index f4c1a05e4a..c14037fd3b 100644 --- a/crates/kilt-dip-support/src/lib.rs +++ b/crates/kilt-dip-support/src/lib.rs @@ -20,77 +20,440 @@ #![cfg_attr(not(feature = "std"), no_std)] +use parity_scale_codec::{Codec, Decode, Encode, HasCompact}; +use scale_info::TypeInfo; +use sp_core::{Get, RuntimeDebug, U256}; +use sp_runtime::{ + generic::Header, + traits::{AtLeast32BitUnsigned, CheckedSub, Hash, MaybeDisplay, Member, SimpleBitOps}, +}; +use sp_std::{borrow::Borrow, marker::PhantomData, vec::Vec}; + +use ::did::{did_details::DidVerificationKey, DidVerificationKeyRelationship}; use pallet_dip_consumer::traits::IdentityProofVerifier; -use sp_std::marker::PhantomData; -use crate::did::MerkleLeavesAndDidSignature; +use crate::{ + did::{RevealedDidKeysAndSignature, RevealedDidKeysSignatureAndCallVerifier, TimeBoundDidSignature}, + merkle::{DidMerkleProof, DidMerkleProofVerifier, RevealedDidMerkleProofLeaf, RevealedDidMerkleProofLeaves}, + state_proofs::{parachain::DipIdentityCommitmentProofVerifier, relay_chain::ParachainHeadProofVerifier}, + traits::{ + Bump, DidSignatureVerifierContext, DipCallOriginFilter, HistoricalBlockRegistry, ProviderParachainStateInfo, + RelayChainStorageInfo, + }, + utils::OutputOf, +}; pub mod did; pub mod merkle; +pub mod state_proofs; pub mod traits; -pub mod xcm; - -/// A type that chains a Merkle proof verification with a DID signature -/// verification. The required input of this type is a tuple (A, B) where A is -/// the type of input required by the `MerkleProofVerifier` and B is a -/// `DidSignature`. -/// The successful output of this type is the output type of the -/// `MerkleProofVerifier`, meaning that DID signature verification happens -/// internally and does not transform the result in any way. -pub struct MerkleProofAndDidSignatureVerifier( - PhantomData<(BlockNumber, MerkleProofVerifier, DidSignatureVerifier)>, +pub mod utils; + +pub use state_proofs::relay_chain::RococoStateRootsViaRelayStorePallet; + +#[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] +pub struct SiblingParachainDipStateProof< + RelayBlockHeight, + DipMerkleProofBlindedValues, + DipMerkleProofRevealedLeaf, + LocalBlockNumber, +> { + para_state_root: ParachainRootStateProof, + dip_identity_commitment: Vec>, + did: DipMerkleProofAndDidSignature, +} + +#[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo, Clone)] +pub struct ParachainRootStateProof { + relay_block_height: RelayBlockHeight, + proof: Vec>, +} + +#[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] +pub struct DipMerkleProofAndDidSignature { + leaves: DidMerkleProof, + signature: TimeBoundDidSignature, +} + +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +pub struct DipSiblingProviderStateProofVerifier< + RelayChainStateInfo, + SiblingProviderParachainId, + SiblingProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderWeb3Name, + ProviderLinkedAccountId, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, +>( + #[allow(clippy::type_complexity)] + PhantomData<( + RelayChainStateInfo, + SiblingProviderParachainId, + SiblingProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderWeb3Name, + ProviderLinkedAccountId, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + )>, ); -impl IdentityProofVerifier - for MerkleProofAndDidSignatureVerifier -where - BlockNumber: Clone, - MerkleProofVerifier: IdentityProofVerifier, - // TODO: get rid of this if possible - MerkleProofVerifier::VerificationResult: Clone, - DidSignatureVerifier: IdentityProofVerifier< +impl< Call, Subject, - Proof = MerkleLeavesAndDidSignature, - IdentityDetails = MerkleProofVerifier::IdentityDetails, - Submitter = MerkleProofVerifier::Submitter, - >, + RelayChainStateInfo, + SiblingProviderParachainId, + SiblingProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderWeb3Name, + ProviderLinkedAccountId, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + > IdentityProofVerifier + for DipSiblingProviderStateProofVerifier< + RelayChainStateInfo, + SiblingProviderParachainId, + SiblingProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderWeb3Name, + ProviderLinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + > where + Call: Encode, + TxSubmitter: Encode, + + RelayChainStateInfo: traits::RelayChainStorageInfo + traits::RelayChainStateInfo, + OutputOf: Ord, + RelayChainStateInfo::BlockNumber: Copy + Into + TryFrom + HasCompact, + RelayChainStateInfo::Key: AsRef<[u8]>, + + SiblingProviderParachainId: Get, + + SiblingProviderStateInfo: + traits::ProviderParachainStateInfo, + OutputOf: Ord + From>, + SiblingProviderStateInfo::BlockNumber: Encode + Clone, + SiblingProviderStateInfo::Commitment: Decode, + SiblingProviderStateInfo::Key: AsRef<[u8]>, + + LocalContextProvider: DidSignatureVerifierContext, + LocalContextProvider::BlockNumber: Encode + CheckedSub + From + PartialOrd, + LocalContextProvider::Hash: Encode, + LocalContextProvider::SignedExtra: Encode, + LocalDidDetails: Bump + Default + Encode, + LocalDidCallVerifier: DipCallOriginFilter, + + ProviderDipMerkleHasher: sp_core::Hasher, + ProviderDidKeyId: Encode + Clone + Into, + ProviderLinkedAccountId: Encode + Clone, + ProviderWeb3Name: Encode + Clone, { - // FIXME: Better error handling type Error = (); - // FIXME: Better type declaration - type Proof = MerkleLeavesAndDidSignature; - type IdentityDetails = DidSignatureVerifier::IdentityDetails; - type Submitter = MerkleProofVerifier::Submitter; - type VerificationResult = MerkleProofVerifier::VerificationResult; + type IdentityDetails = LocalDidDetails; + type Proof = SiblingParachainDipStateProof< + RelayChainStateInfo::BlockNumber, + Vec>, + RevealedDidMerkleProofLeaf< + ProviderDidKeyId, + SiblingProviderStateInfo::BlockNumber, + ProviderWeb3Name, + ProviderLinkedAccountId, + >, + LocalContextProvider::BlockNumber, + >; + type Submitter = TxSubmitter; + type VerificationResult = RevealedDidMerkleProofLeaves< + ProviderDidKeyId, + SiblingProviderStateInfo::BlockNumber, + ProviderWeb3Name, + ProviderLinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + >; fn verify_proof_for_call_against_details( call: &Call, subject: &Subject, submitter: &Self::Submitter, - identity_details: &mut Self::IdentityDetails, - proof: &Self::Proof, + identity_details: &mut Option, + proof: Self::Proof, ) -> Result { - let merkle_proof_verification = MerkleProofVerifier::verify_proof_for_call_against_details( + // 1. Verify relay chain proof. + let provider_parachain_header = ParachainHeadProofVerifier::::verify_proof_for_parachain( + &SiblingProviderParachainId::get(), + &proof.para_state_root.relay_block_height, + proof.para_state_root.proof, + )?; + + // 2. Verify parachain state proof. + let subject_identity_commitment = + DipIdentityCommitmentProofVerifier::::verify_proof_for_identifier( + subject, + provider_parachain_header.state_root.into(), + proof.dip_identity_commitment, + )?; + + // 3. Verify DIP merkle proof. + let proof_leaves = DidMerkleProofVerifier::< + ProviderDipMerkleHasher, + _, + _, + _, + _, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + >::verify_dip_merkle_proof(&subject_identity_commitment, proof.did.leaves)?; + + // 4. Verify DID signature. + RevealedDidKeysSignatureAndCallVerifier::< + _, + _, + _, + _, + LocalContextProvider, + _, + _, + LocalDidCallVerifier, + >::verify_did_signature_for_call( call, - subject, submitter, identity_details, - &proof.merkle_leaves, - ) - .map_err(|_| ())?; - DidSignatureVerifier::verify_proof_for_call_against_details( + RevealedDidKeysAndSignature { + merkle_leaves: proof_leaves.borrow(), + did_signature: proof.did.signature, + }, + )?; + + Ok(proof_leaves) + } +} + +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +pub struct ChildParachainDipStateProof< + ParentBlockHeight: Copy + Into + TryFrom, + ParentBlockHasher: Hash, + DipMerkleProofBlindedValues, + DipMerkleProofRevealedLeaf, +> { + para_state_root: ParachainRootStateProof, + relay_header: Header, + dip_identity_commitment: Vec>, + did: DipMerkleProofAndDidSignature, +} + +pub struct DipChildProviderStateProofVerifier< + RelayChainInfo, + ChildProviderParachainId, + ChildProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderWeb3Name, + ProviderLinkedAccountId, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, +>( + #[allow(clippy::type_complexity)] + PhantomData<( + RelayChainInfo, + ChildProviderParachainId, + ChildProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderWeb3Name, + ProviderLinkedAccountId, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + )>, +); + +impl< + Call, + Subject, + RelayChainInfo, + ChildProviderParachainId, + ChildProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderWeb3Name, + ProviderLinkedAccountId, + const MAX_REVEALED_KEYS_COUNT: u32, + const MAX_REVEALED_ACCOUNTS_COUNT: u32, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + > IdentityProofVerifier + for DipChildProviderStateProofVerifier< + RelayChainInfo, + ChildProviderParachainId, + ChildProviderStateInfo, + TxSubmitter, + ProviderDipMerkleHasher, + ProviderDidKeyId, + ProviderWeb3Name, + ProviderLinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + LocalDidDetails, + LocalContextProvider, + LocalDidCallVerifier, + > where + Call: Encode, + TxSubmitter: Encode, + + RelayChainInfo: RelayChainStorageInfo + + HistoricalBlockRegistry< + BlockNumber = ::BlockNumber, + Hasher = ::Hasher, + >, + OutputOf<::Hasher>: + Ord + Default + sp_std::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec, + ::BlockNumber: Copy + + Into + + TryFrom + + HasCompact + + Member + + sp_std::hash::Hash + + MaybeDisplay + + AtLeast32BitUnsigned + + Codec, + RelayChainInfo::Key: AsRef<[u8]>, + + ChildProviderParachainId: Get, + + ChildProviderStateInfo: ProviderParachainStateInfo, + OutputOf: Ord + From::Hasher>>, + ChildProviderStateInfo::BlockNumber: Encode + Clone, + ChildProviderStateInfo::Commitment: Decode, + ChildProviderStateInfo::Key: AsRef<[u8]>, + + LocalContextProvider: + DidSignatureVerifierContext::BlockNumber>, + LocalContextProvider::BlockNumber: CheckedSub + From, + LocalContextProvider::Hash: Encode, + LocalContextProvider::SignedExtra: Encode, + LocalDidDetails: Bump + Default + Encode, + LocalDidCallVerifier: DipCallOriginFilter, + + ProviderDipMerkleHasher: sp_core::Hasher, + ProviderDidKeyId: Encode + Clone + Into, + ProviderLinkedAccountId: Encode + Clone, + ProviderWeb3Name: Encode + Clone, +{ + type Error = (); + type IdentityDetails = LocalDidDetails; + type Proof = ChildParachainDipStateProof< + ::BlockNumber, + ::Hasher, + Vec>, + RevealedDidMerkleProofLeaf< + ProviderDidKeyId, + ChildProviderStateInfo::BlockNumber, + ProviderWeb3Name, + ProviderLinkedAccountId, + >, + >; + type Submitter = TxSubmitter; + type VerificationResult = RevealedDidMerkleProofLeaves< + ProviderDidKeyId, + ChildProviderStateInfo::BlockNumber, + ProviderWeb3Name, + ProviderLinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + >; + + fn verify_proof_for_call_against_details( + call: &Call, + subject: &Subject, + submitter: &Self::Submitter, + identity_details: &mut Option, + proof: Self::Proof, + ) -> Result { + // 1. Retrieve block hash from provider at the proof height + let block_hash_at_height = + RelayChainInfo::block_hash_for(&proof.para_state_root.relay_block_height).ok_or(())?; + + // 1.1 Verify that the provided header hashes to the same block has retrieved + if block_hash_at_height != proof.relay_header.hash() { + return Err(()); + } + // 1.2 If so, extract the state root from the header + let state_root_at_height = proof.relay_header.state_root; + + // FIXME: Compilation error + // 2. Verify relay chain proof + let provider_parachain_header = + ParachainHeadProofVerifier::::verify_proof_for_parachain_with_root( + &ChildProviderParachainId::get(), + &state_root_at_height, + proof.para_state_root.proof, + )?; + + // 3. Verify parachain state proof. + let subject_identity_commitment = + DipIdentityCommitmentProofVerifier::::verify_proof_for_identifier( + subject, + provider_parachain_header.state_root.into(), + proof.dip_identity_commitment, + )?; + + // 4. Verify DIP merkle proof. + let proof_leaves = DidMerkleProofVerifier::< + ProviderDipMerkleHasher, + _, + _, + _, + _, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + >::verify_dip_merkle_proof(&subject_identity_commitment, proof.did.leaves)?; + + // 5. Verify DID signature. + RevealedDidKeysSignatureAndCallVerifier::< + _, + _, + _, + _, + LocalContextProvider, + _, + _, + LocalDidCallVerifier, + >::verify_did_signature_for_call( call, - subject, submitter, identity_details, - // FIXME: Remove `clone()` requirement - &MerkleLeavesAndDidSignature { - merkle_leaves: merkle_proof_verification.clone(), - did_signature: proof.did_signature.clone(), + RevealedDidKeysAndSignature { + merkle_leaves: proof_leaves.borrow(), + did_signature: proof.did.signature, }, - ) - .map_err(|_| ())?; - Ok(merkle_proof_verification) + )?; + Ok(proof_leaves) } } diff --git a/crates/kilt-dip-support/src/merkle.rs b/crates/kilt-dip-support/src/merkle.rs index 01722d5555..66bebe90eb 100644 --- a/crates/kilt-dip-support/src/merkle.rs +++ b/crates/kilt-dip-support/src/merkle.rs @@ -18,18 +18,15 @@ use did::{did_details::DidPublicKeyDetails, DidVerificationKeyRelationship}; use frame_support::{traits::ConstU32, RuntimeDebug}; -use pallet_dip_consumer::{identity::IdentityDetails, traits::IdentityProofVerifier}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_runtime::{BoundedVec, SaturatedConversion}; use sp_std::{fmt::Debug, marker::PhantomData, vec::Vec}; use sp_trie::{verify_trie_proof, LayoutV1}; -pub type BlindedValue = Vec; - -#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo, Default)] -pub struct MerkleProof { - pub blinded: BlindedValue, +#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, Default, TypeInfo)] +pub struct DidMerkleProof { + pub blinded: BlindedValues, // TODO: Probably replace with a different data structure for better lookup capabilities pub revealed: Vec, } @@ -86,7 +83,7 @@ impl From for Web3NameMerkleKey { } } #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct Web3NameMerkleValue(BlockNumber); +pub struct Web3NameMerkleValue(pub BlockNumber); impl From for Web3NameMerkleValue { fn from(value: BlockNumber) -> Self { @@ -113,7 +110,7 @@ impl From<()> for LinkedAccountMerkleValue { } #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub enum ProofLeaf { +pub enum RevealedDidMerkleProofLeaf { // The key and value for the leaves of a merkle proof that contain a reference // (by ID) to the key details, provided in a separate leaf. DidKey(DidKeyMerkleKey, DidKeyMerkleValue), @@ -121,7 +118,8 @@ pub enum ProofLeaf { LinkedAccount(LinkedAccountMerkleKey, LinkedAccountMerkleValue), } -impl ProofLeaf +impl + RevealedDidMerkleProofLeaf where KeyId: Encode, Web3Name: Encode, @@ -129,22 +127,23 @@ where { pub fn encoded_key(&self) -> Vec { match self { - ProofLeaf::DidKey(key, _) => key.encode(), - ProofLeaf::Web3Name(key, _) => key.encode(), - ProofLeaf::LinkedAccount(key, _) => key.encode(), + RevealedDidMerkleProofLeaf::DidKey(key, _) => key.encode(), + RevealedDidMerkleProofLeaf::Web3Name(key, _) => key.encode(), + RevealedDidMerkleProofLeaf::LinkedAccount(key, _) => key.encode(), } } } -impl ProofLeaf +impl + RevealedDidMerkleProofLeaf where BlockNumber: Encode, { pub fn encoded_value(&self) -> Vec { match self { - ProofLeaf::DidKey(_, value) => value.encode(), - ProofLeaf::Web3Name(_, value) => value.encode(), - ProofLeaf::LinkedAccount(_, value) => value.encode(), + RevealedDidMerkleProofLeaf::DidKey(_, value) => value.encode(), + RevealedDidMerkleProofLeaf::Web3Name(_, value) => value.encode(), + RevealedDidMerkleProofLeaf::LinkedAccount(_, value) => value.encode(), } } } @@ -163,7 +162,7 @@ pub struct RevealedWeb3Name { } #[derive(Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen, Encode, Decode, Default)] -pub struct VerificationResult< +pub struct RevealedDidMerkleProofLeaves< KeyId, BlockNumber, Web3Name, @@ -183,8 +182,8 @@ impl< LinkedAccountId, const MAX_REVEALED_KEYS_COUNT: u32, const MAX_REVEALED_ACCOUNTS_COUNT: u32, - > AsRef<[RevealedDidKey]> - for VerificationResult< + > sp_std::borrow::Borrow<[RevealedDidKey]> + for RevealedDidMerkleProofLeaves< KeyId, BlockNumber, Web3Name, @@ -193,91 +192,63 @@ impl< MAX_REVEALED_ACCOUNTS_COUNT, > { - fn as_ref(&self) -> &[RevealedDidKey] { - self.did_keys.as_ref() + fn borrow(&self) -> &[RevealedDidKey] { + self.did_keys.borrow() } } /// A type that verifies a Merkle proof that reveals some leaves representing /// keys in a DID Document. /// Can also be used on its own, without any DID signature verification. -pub struct DidMerkleProofVerifier< +pub(crate) struct DidMerkleProofVerifier< Hasher, - AccountId, KeyId, BlockNumber, - Details, Web3Name, LinkedAccountId, const MAX_REVEALED_KEYS_COUNT: u32, const MAX_REVEALED_ACCOUNTS_COUNT: u32, ->( - #[allow(clippy::type_complexity)] - PhantomData<( - Hasher, - AccountId, - KeyId, - BlockNumber, - Details, - Web3Name, - LinkedAccountId, - ConstU32, - ConstU32, - )>, -); +>(#[allow(clippy::type_complexity)] PhantomData<(Hasher, KeyId, BlockNumber, Web3Name, LinkedAccountId)>); impl< - Call, - Subject, Hasher, - AccountId, KeyId, BlockNumber, - Details, Web3Name, LinkedAccountId, const MAX_REVEALED_KEYS_COUNT: u32, const MAX_REVEALED_ACCOUNTS_COUNT: u32, - > IdentityProofVerifier - for DidMerkleProofVerifier< + > + DidMerkleProofVerifier< Hasher, - AccountId, KeyId, BlockNumber, - Details, Web3Name, LinkedAccountId, MAX_REVEALED_KEYS_COUNT, MAX_REVEALED_ACCOUNTS_COUNT, > where - // TODO: Remove `Debug` bound - BlockNumber: Encode + Clone + Debug, + BlockNumber: Encode + Clone, Hasher: sp_core::Hasher, - KeyId: Encode + Clone + Ord + Into, + KeyId: Encode + Clone, LinkedAccountId: Encode + Clone, Web3Name: Encode + Clone, { - // TODO: Proper error handling - type Error = (); - type Proof = MerkleProof>, ProofLeaf>; - type IdentityDetails = IdentityDetails; - type Submitter = AccountId; - type VerificationResult = VerificationResult< - KeyId, - BlockNumber, - Web3Name, - LinkedAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - >; - - fn verify_proof_for_call_against_details( - _call: &Call, - _subject: &Subject, - _submitter: &Self::Submitter, - identity_details: &mut Self::IdentityDetails, - proof: &Self::Proof, - ) -> Result { + #[allow(clippy::result_unit_err)] + pub(crate) fn verify_dip_merkle_proof( + identity_commitment: &Hasher::Out, + proof: DidMerkleProof>, RevealedDidMerkleProofLeaf>, + ) -> Result< + RevealedDidMerkleProofLeaves< + KeyId, + BlockNumber, + Web3Name, + LinkedAccountId, + MAX_REVEALED_KEYS_COUNT, + MAX_REVEALED_ACCOUNTS_COUNT, + >, + (), + > { // TODO: more efficient by removing cloning and/or collecting. // Did not find another way of mapping a Vec<(Vec, Vec)> to a // Vec<(Vec, Option>)>. @@ -286,12 +257,8 @@ impl< .iter() .map(|leaf| (leaf.encoded_key(), Some(leaf.encoded_value()))) .collect::, Option>)>>(); - verify_trie_proof::, _, _, _>( - &identity_details.digest.clone().into(), - &proof.blinded, - &proof_leaves, - ) - .map_err(|_| ())?; + verify_trie_proof::, _, _, _>(identity_commitment, &proof.blinded, &proof_leaves) + .map_err(|_| ())?; // At this point, we know the proof is valid. We just need to map the revealed // leaves to something the consumer can easily operate on. @@ -307,7 +274,7 @@ impl< BoundedVec::with_bounded_capacity(MAX_REVEALED_ACCOUNTS_COUNT.saturated_into()), ), |(mut keys, web3_name, mut linked_accounts), leaf| match leaf { - ProofLeaf::DidKey(key_id, key_value) => { + RevealedDidMerkleProofLeaf::DidKey(key_id, key_value) => { keys.try_push(RevealedDidKey { // TODO: Avoid cloning if possible id: key_id.0.clone(), @@ -318,7 +285,7 @@ impl< Ok::<_, ()>((keys, web3_name, linked_accounts)) } // TODO: Avoid cloning if possible - ProofLeaf::Web3Name(revealed_web3_name, details) => Ok(( + RevealedDidMerkleProofLeaf::Web3Name(revealed_web3_name, details) => Ok(( keys, Some(RevealedWeb3Name { web3_name: revealed_web3_name.0.clone(), @@ -326,14 +293,14 @@ impl< }), linked_accounts, )), - ProofLeaf::LinkedAccount(account_id, _) => { + RevealedDidMerkleProofLeaf::LinkedAccount(account_id, _) => { linked_accounts.try_push(account_id.0.clone()).map_err(|_| ())?; Ok::<_, ()>((keys, web3_name, linked_accounts)) } }, )?; - Ok(VerificationResult { + Ok(RevealedDidMerkleProofLeaves { did_keys, web3_name, linked_accounts, diff --git a/crates/kilt-dip-support/src/state_proofs.rs b/crates/kilt-dip-support/src/state_proofs.rs new file mode 100644 index 0000000000..0e8bf02562 --- /dev/null +++ b/crates/kilt-dip-support/src/state_proofs.rs @@ -0,0 +1,341 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 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 parity_scale_codec::{Decode, Encode, HasCompact}; +use sp_core::{storage::StorageKey, U256}; +use sp_runtime::generic::Header; +use sp_std::{marker::PhantomData, vec::Vec}; +use sp_trie::StorageProof; + +use crate::utils::OutputOf; + +use substrate_no_std_port::read_proof_check; + +// Ported from https://github.com/paritytech/substrate/blob/b27c470eaff379f512d1dec052aff5d551ed3b03/primitives/state-machine/src/lib.rs#L1076 +// Needs to be replaced with its runtime-friendly version when available, or be +// kept up-to-date with upstream. +mod substrate_no_std_port { + use super::*; + + use hash_db::EMPTY_PREFIX; + use parity_scale_codec::Codec; + use sp_core::Hasher; + use sp_state_machine::{Backend, TrieBackend, TrieBackendBuilder}; + use sp_std::collections::btree_map::BTreeMap; + use sp_trie::{HashDBT, MemoryDB}; + + pub(super) fn read_proof_check( + root: H::Out, + proof: StorageProof, + keys: I, + ) -> Result, Option>>, ()> + where + H: Hasher, + H::Out: Ord + Codec, + I: IntoIterator, + I::Item: AsRef<[u8]>, + { + let proving_backend = create_proof_check_backend::(root, proof)?; + let mut result = BTreeMap::new(); + for key in keys.into_iter() { + let value = read_proof_check_on_proving_backend(&proving_backend, key.as_ref())?; + result.insert(key.as_ref().to_vec(), value); + } + Ok(result) + } + + fn read_proof_check_on_proving_backend( + proving_backend: &TrieBackend, H>, + key: &[u8], + ) -> Result>, ()> + where + H: Hasher, + H::Out: Ord + Codec, + { + proving_backend.storage(key).map_err(|_| ()) + } + + fn create_proof_check_backend(root: H::Out, proof: StorageProof) -> Result, H>, ()> + where + H: Hasher, + H::Out: Codec, + { + let db = proof.into_memory_db(); + + if db.contains(&root, EMPTY_PREFIX) { + Ok(TrieBackendBuilder::new(db, root).build()) + } else { + Err(()) + } + } +} + +pub(super) mod relay_chain { + use super::*; + + use sp_runtime::traits::BlakeTwo256; + + use crate::traits::{RelayChainStateInfo, RelayChainStorageInfo}; + + pub struct ParachainHeadProofVerifier(PhantomData); + + // Uses the provided `root` to verify the proof. + impl ParachainHeadProofVerifier + where + RelayChainState: RelayChainStorageInfo, + OutputOf: Ord, + RelayChainState::BlockNumber: Copy + Into + TryFrom + HasCompact, + RelayChainState::Key: AsRef<[u8]>, + { + pub fn verify_proof_for_parachain_with_root( + para_id: &RelayChainState::ParaId, + root: &OutputOf<::Hasher>, + proof: impl IntoIterator>, + ) -> Result, ()> { + let parachain_storage_key = RelayChainState::parachain_head_storage_key(para_id); + let storage_proof = StorageProof::new(proof); + let revealed_leaves = + read_proof_check::(*root, storage_proof, [¶chain_storage_key].iter()) + .map_err(|_| ())?; + // TODO: Remove at some point + { + debug_assert!(revealed_leaves.len() == 1usize); + debug_assert!(revealed_leaves.contains_key(parachain_storage_key.as_ref())); + } + let Some(Some(encoded_head)) = revealed_leaves.get(parachain_storage_key.as_ref()) else { return Err(()) }; + // TODO: Figure out why RPC call returns 2 bytes in front which we don't need + let mut unwrapped_head = &encoded_head[2..]; + Header::decode(&mut unwrapped_head).map_err(|_| ()) + } + } + + // Relies on the `RelayChainState::state_root_for_block` to retrieve the state + // root for the given block. + impl ParachainHeadProofVerifier + where + RelayChainState: RelayChainStateInfo, + OutputOf: Ord, + RelayChainState::BlockNumber: Copy + Into + TryFrom + HasCompact, + RelayChainState::Key: AsRef<[u8]>, + { + #[allow(clippy::result_unit_err)] + pub fn verify_proof_for_parachain( + para_id: &RelayChainState::ParaId, + relay_height: &RelayChainState::BlockNumber, + proof: impl IntoIterator>, + ) -> Result, ()> { + let relay_state_root = RelayChainState::state_root_for_block(relay_height).ok_or(())?; + Self::verify_proof_for_parachain_with_root(para_id, &relay_state_root, proof) + } + } + + pub struct RococoStateRootsViaRelayStorePallet(PhantomData); + + impl RelayChainStorageInfo for RococoStateRootsViaRelayStorePallet + where + Runtime: pallet_relay_store::Config, + { + type BlockNumber = u32; + type Hasher = BlakeTwo256; + type Key = StorageKey; + type ParaId = u32; + + fn parachain_head_storage_key(para_id: &Self::ParaId) -> Self::Key { + // TODO: It's not possible to access the runtime definition from here. + let encoded_para_id = para_id.encode(); + let storage_key = [ + frame_support::storage::storage_prefix(b"Paras", b"Heads").as_slice(), + sp_io::hashing::twox_64(&encoded_para_id).as_slice(), + encoded_para_id.as_slice(), + ] + .concat(); + StorageKey(storage_key) + } + } + + impl RelayChainStateInfo for RococoStateRootsViaRelayStorePallet + where + Runtime: pallet_relay_store::Config, + { + fn state_root_for_block(block_height: &Self::BlockNumber) -> Option> { + pallet_relay_store::Pallet::::latest_relay_head_for_block(block_height) + .map(|relay_header| relay_header.relay_parent_storage_root) + } + } + + #[cfg(test)] + mod polkadot_parachain_head_proof_verifier_tests { + use super::*; + + use hex_literal::hex; + use sp_runtime::traits::BlakeTwo256; + + // Polkadot block n: 16_363_919, + // hash 0x18e90e9aa8e3b063f60386ba1b0415111798e72d01de58b1438d620d42f58e39 + struct StaticPolkadotInfoProvider; + + impl RelayChainStorageInfo for StaticPolkadotInfoProvider { + type BlockNumber = u32; + type Hasher = BlakeTwo256; + type Key = StorageKey; + type ParaId = u32; + + fn parachain_head_storage_key(para_id: &Self::ParaId) -> Self::Key { + // Adapted from https://github.com/polytope-labs/substrate-ismp/blob/7fb09da6c7b818a98c25c962fee0ddde8e737306/parachain/src/consensus.rs#L369 + // Used for testing. In production this would be generated from the relay + // runtime definition of the `paras` storage map. + let encoded_para_id = para_id.encode(); + let storage_key = [ + frame_support::storage::storage_prefix(b"Paras", b"Heads").as_slice(), + sp_io::hashing::twox_64(&encoded_para_id).as_slice(), + encoded_para_id.as_slice(), + ] + .concat(); + StorageKey(storage_key) + } + } + + impl RelayChainStateInfo for StaticPolkadotInfoProvider { + fn state_root_for_block(_block_height: &Self::BlockNumber) -> Option> { + Some(hex!("81b75d95075d16005ee0a987a3f061d3011ada919b261e9b02961b9b3725f3fd").into()) + } + } + + #[test] + fn test_spiritnet_head_proof() { + // As of RPC state_getReadProof("0xcd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c32c0cfd6c23b92a7826080000", "0x18e90e9aa8e3b063f60386ba1b0415111798e72d01de58b1438d620d42f58e39") + let spiritnet_head_proof_at_block = [ + hex!("570c0cfd6c23b92a7826080000f102e90265541097fb02782e14f43074f0b00e44ae8e9fe426982323ef1d329739740d37f252ff006d1156941db1bccd58ce3a1cac4f40cad91f692d94e98f501dd70081a129b69a3e2ef7e1ff84ba3d86dab4e95f2c87f6b1055ebd48519c185360eae58f05d1ea08066175726120dcdc6308000000000561757261010170ccfaf3756d1a8dd8ae5c89094199d6d32e5dd9f0920f6fe30f986815b5e701974ea0e0e0a901401f2c72e3dd8dbdf4aa55d59bf3e7021856cdb8038419eb8c").to_vec(), + hex!("80046480186b1513c5112466ada33da3c65558979906ca9fb82510b62f6ea01f550a4807808bc90ded5636f31c8395a315b5f27a1a25a2ceebd36921a518669ce7e52f80e680993c5e952e6e4f72f295ba04951ace9029b23e9a87887b41895c16f77bec42ee80b798b224c5ee3d668519e75ca98504116f645fb969a5e2653a298b0181f9a694").to_vec(), + hex!("80ffff806ecd86e87715a007ee9b216d8a99a604773014260d51f6552b6fbd7c21786d9c80e23ef51809d6c80c01a6e264ff0d298cce01c1addfdbb0789597b9a6b3f3e4fd80c9c5f0f29d777e2cebcdbd06ddf1c2cfa8ee83524b37ace99d8b7a3aeff039b380da013185503cfefa6c9cc88751993f1f2bf4b8fa4918e876f499fb9405e3206c803a89668f636552a0fb93619913dcc46cf3e087363d532b76a345155a44a46b5180c2e7fc654720b7dcc0316ae1591fde4beb8b853a343b7e5e3ee564d2692c2ee280840f9c4ae7c16ae948828bf50faf062264402e6134d2d6144a5e3ecb0a1e1d9c80f93c2be1ef51fb2032445cc7fbc2023b9e3b8cf8c0d832b464ae48a020bfaa8c8010c63537c9bf58d50c8c0e13c154fd88b2f683e13701901bdc64565aa9b756d580f0b60eaf17fb680827e8a8938c717ac943b85ff373c0fc911e06c34a3a30327280ccb29f1efa59fd7c80a730cb88789a5a256b01fee7e83ac9a3c90da330adc7a480c8e57c547edb33d4b712f017f09d2de2e055f18815669c83eef2f7f3e5dcafae80b7b7e7ffc91a7dd4c4902f7f15cd7598d1258a75433ea953565661d114e2dcca80ebc3a2df819c7c2fd1a33eb1d484beaf7b71114d6a6db240d8b07dc10bfdc49b80a71f21aa3fa5d7475bf134d50f25e2515c797d0a4c2e93998888150c1f969ab8801e32613f54e70c95e9b16a14f5797522ef5e2ef7867868ff663e33e8880994ed").to_vec(), + hex!("9e710b30bd2eab0352ddcc26417aa1945fd380d49ebc7ca5c1b751c2badb5e5a326d3ba9e331d8b7c6cf279ed7fd71a8882b6c8038088652f73dc8a22336d10f492f0ef8836beaba0ccfeb0f8fabdc9df1d17e2d807f88402cbbed7fa3307e07044200b572d5e8e12913b41e1923dcb2c0799bc2be804d57e9a8e4934fab698a9db50682052ee9459c666a075d1bfc471da8e5da14da80b9aee043e378f8313e68a6030679ccf3880fa1e7ab19b6244b5c262b7a152f004c5f03c716fb8fff3de61a883bb76adb34a2040080f282bc12648ffb197ffc257edc7ff3a3fdda452daa51091ccbd2dfb91d8aa9518008a0c609ab4888f02c2545c002153297c2641c5a7b4f3d8e25c634e721f80bea80b6617c764df278313c426c46961ccde8ee7a03f9007b74bc8bc6c49d1583cf7d8077b493d45eb153353026cc330307e0753ac41a5cb8e843ceb1efdc46655f33a0808bdaa43fc5dc0e928e2da0ce8ed02096b0b74c61feaba2546980ed9c6174f71d").to_vec(), + hex!("9f0b3c252fcb29d88eff4f3de5de4476c3ffbf8013c601cc93de3437f9d415bd52c48d794b341f218b9d0020a4b646746c24d0ca80348b8e2c39c479a146933297f62b7051df82e92e1bca761432c3e6f64c74033f80220131e7cd7a08b97f8aa06225f7aefbbca8118fb436c07689c552ed3f577145806d974dd9e4db5e407e29f84c4121ccc58f9c6adc3933afc1bcaef52defe77de5801e9e1a21db053de56365fdee57998488ddae7d664c0430da90469dde17936c1f80c5c11751bbfc99a1ad805c58a65b9704e0bad58e694023e9cc57ce6ef84cdb0b8038f6c242700eaea04ffad5c25ca9a9b1cc2af7303655a32eb59e84b6bb927cd3802575469e76e104b0db8b18dbc762b997a78aa666432a44c4b955ced044a4691f80a81408b856272feeec08845af515e27d033efd3ff8b46de6bc706c38e600086a809ee78332c2a38a3918070942421e651e0b9a43e4b8b2c92e87a2552cede73e8380c9d79f411f742cad0c6f2b070aa08703a04cb7db840c3821a6762837dd8d00e9807dcfbc7f2fcc9415e2cb40eef7f718758d76193f325b3f8b7180e3e5e7d6b81e8036252cae6d24a531a151ce1ee223a07bf71cf82a7fdf49090e4ca345d27d68ca80e3f08ef11671f8f1defa66fa2af71e1a871430e9352df9b3f1d427b5a5dabfb280b51d28c9b99030d050fc1578cd23b5506e769b86c8f4ccc6aded4b8d7c1a73b7").to_vec(), + ].to_vec(); + // As of query paras::heads(2_086) at block + // "0x18e90e9aa8e3b063f60386ba1b0415111798e72d01de58b1438d620d42f58e39" + // (16_363_919) which results in the key + // "0xcd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c32c0cfd6c23b92a7826080000" + // + let expected_spiritnet_head_at_block = hex!("65541097fb02782e14f43074f0b00e44ae8e9fe426982323ef1d329739740d37f252ff006d1156941db1bccd58ce3a1cac4f40cad91f692d94e98f501dd70081a129b69a3e2ef7e1ff84ba3d86dab4e95f2c87f6b1055ebd48519c185360eae58f05d1ea08066175726120dcdc6308000000000561757261010170ccfaf3756d1a8dd8ae5c89094199d6d32e5dd9f0920f6fe30f986815b5e701974ea0e0e0a901401f2c72e3dd8dbdf4aa55d59bf3e7021856cdb8038419eb8c").to_vec(); + let returned_head = ParachainHeadProofVerifier::::verify_proof_for_parachain( + &2_086, + &16_363_919, + spiritnet_head_proof_at_block, + ) + .expect("Parachain head proof verification should not fail."); + assert!(returned_head.encode() == expected_spiritnet_head_at_block, "Parachain head returned from the state proof verification should not be different than the pre-computed one."); + } + } +} + +pub(super) mod parachain { + use super::*; + + use crate::traits::ProviderParachainStateInfo; + + pub struct DipIdentityCommitmentProofVerifier(PhantomData); + + impl DipIdentityCommitmentProofVerifier + where + ParaInfo: ProviderParachainStateInfo, + OutputOf: Ord, + ParaInfo::Commitment: Decode, + ParaInfo::Key: AsRef<[u8]>, + { + #[allow(clippy::result_unit_err)] + pub fn verify_proof_for_identifier( + identifier: &ParaInfo::Identifier, + state_root: OutputOf, + proof: impl IntoIterator>, + ) -> Result { + let dip_commitment_storage_key = ParaInfo::dip_subject_storage_key(identifier); + let storage_proof = StorageProof::new(proof); + let revealed_leaves = read_proof_check::( + state_root, + storage_proof, + [&dip_commitment_storage_key].iter(), + ) + .map_err(|_| ())?; + // TODO: Remove at some point + { + debug_assert!(revealed_leaves.len() == 1usize); + debug_assert!(revealed_leaves.contains_key(dip_commitment_storage_key.as_ref())); + } + let Some(Some(encoded_commitment)) = revealed_leaves.get(dip_commitment_storage_key.as_ref()) else { return Err(()) }; + ParaInfo::Commitment::decode(&mut &encoded_commitment[..]).map_err(|_| ()) + } + } + + #[cfg(test)] + mod spiritnet_test_event_count_value { + use super::*; + + use hex_literal::hex; + use sp_core::H256; + use sp_runtime::traits::BlakeTwo256; + + // Spiritnet block n: 4_184_668, + // hash 0x2c0746e7e9ccc6e4d27bcb4118cb6821ae53ae9bf372f4f49ac28d8598f9bed5 + struct StaticSpiritnetInfoProvider; + + // We use the `system::eventCount()` storage entry as a unit test here. + impl ProviderParachainStateInfo for StaticSpiritnetInfoProvider { + type BlockNumber = u32; + // The type of the `eventCount()` storage entry. + type Commitment = u32; + type Hasher = BlakeTwo256; + // Irrelevant for this test here + type Identifier = (); + type Key = StorageKey; + + fn dip_subject_storage_key(_identifier: &Self::Identifier) -> Self::Key { + // system::eventCount() raw storage key + let storage_key = hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec(); + StorageKey(storage_key) + } + } + + #[test] + fn test_spiritnet_event_count() { + // As of RPC state_getReadProof(" + // 0x26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850", + // "0x2c0746e7e9ccc6e4d27bcb4118cb6821ae53ae9bf372f4f49ac28d8598f9bed5") + let spiritnet_event_count_proof_at_block = [ + hex!("800404645ea5c1b19ab7a04f536c519aca4983ac205cda3f0000000000545e98fdbe9ce6c55837576c60c7af38501005000000").to_vec(), + hex!("80401080481e2bd8085a02c5b58987bce7a69f0b5c7fa651e8e82c5481c94707860be9078067785103d453293707ba847e21df7e35a7a57b8fb929d40465328b6642669fcc").to_vec(), + hex!("80ffff8010623b5a3a9dbc752963d827be0bb855bf3e24258ae09341d5f762e96a836ac180c34b753605e821528756b55b4ddafb742df6e54fbc03ef401d4ebfd6dd4f3e44806f83646e0bf3ca0ac9f2092dea5b0e3caf210cc6b54c3b44a51855a133367a6580b02cde7b1fd3f8d13f698ef6e9daa29b32258d4d97a8947051070a4540aecacd80903d521961849d07ceee132617b8dde96c3ff472f5a9a089d4055ffe7ffd1e988016c29c943c106713bb8f16b776eb7daed005540165696da286cddf6b25d085448019a464010cb746b0589891f72b0eed603d4712b04af46f7bcae724564194801480a305ffe069db7eb21841f75b5939943f62c4abb3a051d530839c5dd935ccbc8a8035d8938b0c856878de1e3fe45a559588b2da52ccf195ab1e3d0aca6ac7bb079d8064019a474a283c19f46ff4652a5e1f636efd4013d3b8a91c49573045c6ff01c0801a191dcb736faddb84889a13c7aa717d260e9b635b30a9eb3907f925a2253d6880f8bc389fc62ca951609bae208b7506bae497623e647424062d1c56cb1f2d2e1c80211a9fb5f8b794f9fbfbdcd4519aa475ecaf9737b4ee513dde275d5fbbe64da080c267d0ead99634e9b9cfbf61a583877e0241ac518e62e909fbb017469de275f780b3059a7226d4b320c25e9b2f8ffe19cf93467e3b306885962c5f34b5671d15fe8092dfba9e30e1bbefab13c792755d06927e6141f7220b7485e5aa40de92401a66").to_vec(), + hex!("9eaa394eea5630e07c48ae0c9558cef7398f8069ef420a0deb5a428c9a08563b28a78874bba09124eecc8d28bf30b0e2ddd310745f04abf5cb34d6244378cddbf18e849d962c000000000736d8e8140100505f0e7b9012096b41c4eb3aaf947f6ea4290800004c5f0684a022a34dd8bfa2baaf44f172b710040180dd3270a03a1a13fc20bcdf24d1aa4ddccc6183db2e2e153b8a68ba8540699a8a80b413dad63538a591f7f2575d287520ee44d7143aa5ec2411969861e1f55a2989804c3f0f541a13980689894db7c60c785dd29e066f213bb29b17aa740682ad7efd8026d3a50544f5c89500745aca2be36cfe076f599c5115192fb9deae227e2710c980bd04b00bf6b42756a06a4fbf05a5231c2094e48182eca95d2cff73ab907592aa").to_vec(), + ].to_vec(); + let spiritnet_state_root: H256 = + hex!("94c23fda279cea4a4370e90f1544c8938923dfd4ac201a420c7a26fb0d3caf8c").into(); + // As of query system::eventCount() at block + // "0x2c0746e7e9ccc6e4d27bcb4118cb6821ae53ae9bf372f4f49ac28d8598f9bed5" which + // results in the key + // "0x26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850" + let expected_event_count_at_block = 5; + let returned_event_count = + DipIdentityCommitmentProofVerifier::::verify_proof_for_identifier( + &(), + spiritnet_state_root, + spiritnet_event_count_proof_at_block, + ) + .unwrap(); + assert!(returned_event_count == expected_event_count_at_block, "Spiritnet event count returned from the state proof verification should not be different than the pre-computed one."); + } + } +} diff --git a/crates/kilt-dip-support/src/traits.rs b/crates/kilt-dip-support/src/traits.rs index 66417991e8..2477d82a4f 100644 --- a/crates/kilt-dip-support/src/traits.rs +++ b/crates/kilt-dip-support/src/traits.rs @@ -16,10 +16,12 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use sp_core::Get; +use sp_core::storage::StorageKey; use sp_runtime::traits::{CheckedAdd, One, Zero}; use sp_std::marker::PhantomData; +use crate::utils::OutputOf; + // TODO: Switch to the `Incrementable` trait once it's added to the root of // `frame_support`. /// A trait for "bumpable" types, i.e., types that have some notion of order of @@ -41,7 +43,7 @@ where /// A trait for types that implement some sort of access control logic on the /// provided input `Call` type. -pub trait DidDipOriginFilter { +pub trait DipCallOriginFilter { /// The error type for cases where the checks fail. type Error; /// The type of additional information required by the type to perform the @@ -53,25 +55,106 @@ pub trait DidDipOriginFilter { fn check_call_origin_info(call: &Call, info: &Self::OriginInfo) -> Result; } -pub struct GenesisProvider(PhantomData); +pub trait RelayChainStorageInfo { + type BlockNumber; + type Hasher: sp_runtime::traits::Hash; + type Key; + type ParaId; + + fn parachain_head_storage_key(para_id: &Self::ParaId) -> Self::Key; +} + +pub trait RelayChainStateInfo: RelayChainStorageInfo { + fn state_root_for_block(block_height: &Self::BlockNumber) -> Option>; +} + +pub trait ProviderParachainStateInfo { + type BlockNumber; + type Commitment; + type Key; + type Hasher: sp_runtime::traits::Hash; + type Identifier; + + fn dip_subject_storage_key(identifier: &Self::Identifier) -> Self::Key; +} + +pub struct ProviderParachainStateInfoViaProviderPallet(PhantomData); -impl Get for GenesisProvider +impl ProviderParachainStateInfo for ProviderParachainStateInfoViaProviderPallet +where + T: pallet_dip_provider::Config, +{ + type BlockNumber = T::BlockNumber; + type Commitment = T::IdentityCommitment; + type Hasher = T::Hashing; + type Identifier = T::Identifier; + type Key = StorageKey; + + fn dip_subject_storage_key(identifier: &Self::Identifier) -> Self::Key { + StorageKey(pallet_dip_provider::IdentityCommitments::::hashed_key_for( + identifier, + )) + } +} + +pub trait DidSignatureVerifierContext { + const SIGNATURE_VALIDITY: u16; + + type BlockNumber; + type Hash; + type SignedExtra; + + fn block_number() -> Self::BlockNumber; + fn genesis_hash() -> Self::Hash; + fn signed_extra() -> Self::SignedExtra; +} + +pub struct FrameSystemDidSignatureContext(PhantomData); + +impl DidSignatureVerifierContext + for FrameSystemDidSignatureContext where T: frame_system::Config, - T::BlockNumber: Zero, { - fn get() -> T::Hash { + const SIGNATURE_VALIDITY: u16 = SIGNATURE_VALIDITY; + + type BlockNumber = T::BlockNumber; + type Hash = T::Hash; + type SignedExtra = (); + + fn block_number() -> Self::BlockNumber { + frame_system::Pallet::::block_number() + } + + fn genesis_hash() -> Self::Hash { frame_system::Pallet::::block_hash(T::BlockNumber::zero()) } + + fn signed_extra() -> Self::SignedExtra {} } -pub struct BlockNumberProvider(PhantomData); +pub trait HistoricalBlockRegistry { + type BlockNumber; + type Hasher: sp_runtime::traits::Hash; + + fn block_hash_for(block: &Self::BlockNumber) -> Option>; +} -impl Get for BlockNumberProvider +impl HistoricalBlockRegistry for T where T: frame_system::Config, { - fn get() -> T::BlockNumber { - frame_system::Pallet::::block_number() + type BlockNumber = T::BlockNumber; + type Hasher = T::Hashing; + + fn block_hash_for(block: &Self::BlockNumber) -> Option> { + let retrieved_block = frame_system::Pallet::::block_hash(block); + let default_block_hash_value = ::default(); + + if retrieved_block == default_block_hash_value { + None + } else { + Some(retrieved_block) + } } } diff --git a/crates/kilt-dip-support/src/utils.rs b/crates/kilt-dip-support/src/utils.rs new file mode 100644 index 0000000000..85fa1334f9 --- /dev/null +++ b/crates/kilt-dip-support/src/utils.rs @@ -0,0 +1,114 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 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 pallet_dip_provider::traits::IdentityProvider; +use sp_std::marker::PhantomData; + +pub struct CombinedIdentityResult { + pub a: OutputA, + pub b: OutputB, + pub c: OutputC, +} + +impl From<(OutputA, OutputB, OutputC)> + for CombinedIdentityResult +{ + fn from(value: (OutputA, OutputB, OutputC)) -> Self { + Self { + a: value.0, + b: value.1, + c: value.2, + } + } +} + +impl CombinedIdentityResult +where + OutputB: Default, + OutputC: Default, +{ + pub fn from_a(a: OutputA) -> Self { + Self { + a, + b: OutputB::default(), + c: OutputC::default(), + } + } +} + +impl CombinedIdentityResult +where + OutputA: Default, + OutputC: Default, +{ + pub fn from_b(b: OutputB) -> Self { + Self { + a: OutputA::default(), + b, + c: OutputC::default(), + } + } +} + +impl CombinedIdentityResult +where + OutputA: Default, + OutputB: Default, +{ + pub fn from_c(c: OutputC) -> Self { + Self { + a: OutputA::default(), + b: OutputB::default(), + c, + } + } +} + +pub struct CombineIdentityFrom(PhantomData<(A, B, C)>); + +impl IdentityProvider for CombineIdentityFrom +where + A: IdentityProvider, + B: IdentityProvider, + C: IdentityProvider, +{ + // TODO: Proper error handling + type Error = (); + type Success = CombinedIdentityResult, Option, Option>; + + fn retrieve(identifier: &Identifier) -> Result, Self::Error> { + match ( + A::retrieve(identifier), + B::retrieve(identifier), + C::retrieve(identifier), + ) { + // If no details is returned, return None for the whole result + (Ok(None), Ok(None), Ok(None)) => Ok(None), + // Otherwise, return `Some` or `None` depending on each result + (Ok(ok_a), Ok(ok_b), Ok(ok_c)) => Ok(Some(CombinedIdentityResult { + a: ok_a, + b: ok_b, + c: ok_c, + })), + // If any of them returns an `Err`, return an `Err` + _ => Err(()), + } + } +} + +pub type OutputOf = ::Output; diff --git a/crates/kilt-dip-support/src/xcm.rs b/crates/kilt-dip-support/src/xcm.rs deleted file mode 100644 index 2f457d7e7b..0000000000 --- a/crates/kilt-dip-support/src/xcm.rs +++ /dev/null @@ -1,289 +0,0 @@ -// KILT Blockchain – https://botlabs.org -// Copyright (C) 2019-2023 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 dip_support::IdentityDetailsAction; -use pallet_dip_provider::traits::{IdentityProofDispatcher, TxBuilder}; -use parity_scale_codec::Encode; -use sp_core::Get; -use sp_std::marker::PhantomData; -use xcm::v3::{ - Instruction::{BuyExecution, DepositAsset, DescendOrigin, ExpectOrigin, RefundSurplus, Transact, WithdrawAsset}, - InteriorMultiLocation, - Junction::AccountId32, - Junctions::{Here, X1}, - MultiAsset, - MultiAssetFilter::Wild, - MultiAssets, MultiLocation, OriginKind, SendError, SendXcm, Weight, - WeightLimit::Limited, - WildMultiAsset::All, - Xcm, -}; - -// Dispatcher using a type implementing the `SendXcm` trait. -// It properly encodes the `Transact` operation, then delegates everything else -// to the sender, similarly to what the XCM pallet's `send` extrinsic does. -pub struct XcmRouterIdentityDispatcher( - PhantomData<(Router, UniversalLocationProvider)>, -); - -impl - IdentityProofDispatcher - for XcmRouterIdentityDispatcher -where - Router: SendXcm, - UniversalLocationProvider: Get, - Identifier: Encode, - ProofOutput: Encode, - AccountId: Into<[u8; 32]> + Clone, -{ - type PreDispatchOutput = Router::Ticket; - type Error = SendError; - - fn pre_dispatch>( - action: IdentityDetailsAction, - source: AccountId, - asset: MultiAsset, - weight: Weight, - destination: MultiLocation, - ) -> Result<(Self::PreDispatchOutput, MultiAssets), Self::Error> { - // TODO: Replace with proper error handling - let dest_tx = Builder::build(destination, action) - .map_err(|_| ()) - .expect("Failed to build call"); - - // TODO: Set an error handler and an appendix to refund any leftover funds to - // the provider parachain sovereign account. - let operation = [[ - ExpectOrigin(Some( - Here.into_location() - .reanchored(&destination, UniversalLocationProvider::get()) - .unwrap(), - )), - DescendOrigin(X1(AccountId32 { - network: None, - id: source.clone().into(), - })), - WithdrawAsset(asset.clone().into()), - BuyExecution { - fees: asset, - weight_limit: Limited(weight), - }, - Transact { - origin_kind: OriginKind::Native, - require_weight_at_most: weight, - call: dest_tx, - }, - RefundSurplus, - DepositAsset { - assets: Wild(All), - beneficiary: MultiLocation { - parents: 1, - // Re-anchor the same account junction as seen from the destination. - // TODO: Error handling - interior: Here - .into_location() - .reanchored(&destination, UniversalLocationProvider::get()) - .unwrap() - .pushed_with_interior(AccountId32 { - network: None, - id: source.into(), - }) - .unwrap() - .interior, - }, - }, - ]] - .concat(); - // TODO: Restructure the trait to be able to inject the [Instruction] provider, - // and unit test that. - debug_assert!(barriers::check_expected_dip_instruction_order(&operation).is_ok()); - let op = Xcm(operation); - Router::validate(&mut Some(destination), &mut Some(op)) - } - - fn dispatch(pre_output: Self::PreDispatchOutput) -> Result<(), Self::Error> { - Router::deliver(pre_output).map(|_| ()) - } -} - -pub mod barriers { - use super::*; - - use frame_support::{ensure, traits::ProcessMessageError}; - use xcm::v3::{Instruction, Junction::Parachain, ParentThen}; - use xcm_executor::traits::ShouldExecute; - - // Must match the order of instructions as produced by the provider's - // implementation of the `IdentityProofDispatcher` trait. - pub(crate) fn check_expected_dip_instruction_order( - instructions: &[Instruction], - ) -> Result<(), ()> { - let mut iter = instructions.iter(); - match ( - iter.next(), - iter.next(), - iter.next(), - iter.next(), - iter.next(), - iter.next(), - iter.next(), - iter.next(), - ) { - ( - // A first instruction different than `DescendOrigin` is needed to distinguish between user-triggered - // and parachain-triggered XCM messages, since also the XCM pallet always preprends user-created XCM - // messages with a `DescendOrigin` instruction. - Some(ExpectOrigin(..)), - // Go down to user level to charge them for the XCM fees. - Some(DescendOrigin(X1(AccountId32 { .. }))), - // Expect the user to first withdraw an asset to pay for the fees. - Some(WithdrawAsset { .. }), - // Buy execution time. - Some(BuyExecution { .. }), - // Although this is irrelevant since `origin_kind` can also be specified by a user, we use - // `OriginKind::Native` here to make clear this is a parachain-dispatched XCM message. - Some(Transact { - origin_kind: OriginKind::Native, - .. - }), - // Any unused weight is refunded. - Some(RefundSurplus), - // Any unused assets are refunded back into the user's account. - Some(DepositAsset { .. }), - // No more instructions are allowed. - None, - ) => Ok(()), - _ => Err(()), - } - } - - // Allows a parachain to descend to an `X1(AccountId32)` junction, withdraw fees - // from their balance, and then carry on with a `Transact`. - // Must be used **ONLY** in conjunction with the `AccountIdJunctionAsParachain` - // origin converter. - pub struct AllowParachainProviderAsSubaccount(PhantomData); - - impl ShouldExecute for AllowParachainProviderAsSubaccount - where - ProviderParaId: Get, - { - fn should_execute( - origin: &MultiLocation, - instructions: &mut [Instruction], - _max_weight: Weight, - _weight_credit: &mut Weight, - ) -> Result<(), ProcessMessageError> { - #[cfg(feature = "std")] - println!( - "AllowParachainProviderAsSubaccount::should_execute(origin = {:?}, instructions = {:?}", - origin, instructions - ); - // Ensure that the origin is a parachain allowed to act as identity provider. - ensure!( - *origin == ParentThen(Parachain(ProviderParaId::get()).into()).into(), - ProcessMessageError::Yield - ); - check_expected_dip_instruction_order(instructions).map_err(|_| ProcessMessageError::Yield) - } - } - - // Decorate an existing barrier to add one more check in case all the previous - // barriers fail. - pub struct OkOrElseCheckForParachainProvider(PhantomData<(Barrier, ProviderParaId)>); - - impl ShouldExecute for OkOrElseCheckForParachainProvider - where - Barrier: ShouldExecute, - ProviderParaId: Get, - { - fn should_execute( - origin: &MultiLocation, - instructions: &mut [Instruction], - max_weight: Weight, - weight_credit: &mut Weight, - ) -> Result<(), ProcessMessageError> { - Barrier::should_execute(origin, instructions, max_weight, weight_credit).or_else(|_| { - AllowParachainProviderAsSubaccount::::should_execute( - origin, - instructions, - max_weight, - weight_credit, - ) - }) - } - } - - // Decorate an existing barrier to check for the provider parachain origin only - // in case none of the previous barriers fail. - pub struct ErrOrElseCheckForParachainProvider(PhantomData<(Barrier, ProviderParaId)>); - - impl ShouldExecute for ErrOrElseCheckForParachainProvider - where - Barrier: ShouldExecute, - ProviderParaId: Get, - { - fn should_execute( - origin: &MultiLocation, - instructions: &mut [Instruction], - max_weight: Weight, - weight_credit: &mut Weight, - ) -> Result<(), ProcessMessageError> { - Barrier::should_execute(origin, instructions, max_weight, weight_credit)?; - AllowParachainProviderAsSubaccount::::should_execute( - origin, - instructions, - max_weight, - weight_credit, - ) - } - } -} - -pub mod origins { - use super::*; - - use xcm::v3::{Junction::Parachain, Junctions::X2}; - use xcm_executor::traits::ConvertOrigin; - - pub struct AccountIdJunctionAsParachain( - PhantomData<(ProviderParaId, ParachainOrigin, RuntimeOrigin)>, - ); - - impl ConvertOrigin - for AccountIdJunctionAsParachain - where - ProviderParaId: Get, - ParachainOrigin: From, - RuntimeOrigin: From, - { - fn convert_origin(origin: impl Into, kind: OriginKind) -> Result { - let origin = origin.into(); - let provider_para_id = ProviderParaId::get(); - match (kind, origin) { - ( - OriginKind::Native, - MultiLocation { - parents: 1, - interior: X2(Parachain(para_id), AccountId32 { .. }), - }, - ) if para_id == provider_para_id => Ok(ParachainOrigin::from(provider_para_id).into()), - _ => Err(origin), - } - } - } -} diff --git a/dip-template/nodes/dip-consumer/src/chain_spec.rs b/dip-template/nodes/dip-consumer/src/chain_spec.rs index 4330bf5c62..bfc123f067 100644 --- a/dip-template/nodes/dip-consumer/src/chain_spec.rs +++ b/dip-template/nodes/dip-consumer/src/chain_spec.rs @@ -99,8 +99,6 @@ fn testnet_genesis( }, aura: Default::default(), aura_ext: Default::default(), - polkadot_xcm: Default::default(), - did_lookup: Default::default(), } } diff --git a/dip-template/nodes/dip-provider/src/chain_spec.rs b/dip-template/nodes/dip-provider/src/chain_spec.rs index af6493f67c..6e162c624e 100644 --- a/dip-template/nodes/dip-provider/src/chain_spec.rs +++ b/dip-template/nodes/dip-provider/src/chain_spec.rs @@ -99,7 +99,6 @@ fn testnet_genesis( }, aura: Default::default(), aura_ext: Default::default(), - polkadot_xcm: Default::default(), did_lookup: Default::default(), } } diff --git a/crates/dip-support/Cargo.toml b/dip-template/pallets/pallet-postit/Cargo.toml similarity index 50% rename from crates/dip-support/Cargo.toml rename to dip-template/pallets/pallet-postit/Cargo.toml index 276b3929ac..dbfc82136b 100644 --- a/crates/dip-support/Cargo.toml +++ b/dip-template/pallets/pallet-postit/Cargo.toml @@ -1,27 +1,40 @@ [package] authors.workspace = true -description = "Support types, traits, and functions for the KILT Decentralized Identity Provider (DIP) functionality." documentation.workspace = true edition.workspace = true homepage.workspace = true license-file.workspace = true -name = "dip-support" readme.workspace = true repository.workspace = true version.workspace = true +name = "pallet-postit" +description = "Simple pallet to store on-chain comments, replies, and likes." + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] [dependencies] -# Parity dependencies +# External dependencies parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} -# Substrate dependencies +#External dependencies frame-support.workspace = true +frame-system.workspace = true +sp-runtime.workspace = true +sp-std.workspace = true [features] default = ["std"] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks" +] std = [ "parity-scale-codec/std", "scale-info/std", - "frame-support/std" + "frame-support/std", + "frame-system/std", + "sp-runtime/std", + "sp-std/std", ] diff --git a/dip-template/pallets/pallet-postit/src/lib.rs b/dip-template/pallets/pallet-postit/src/lib.rs new file mode 100644 index 0000000000..170797f61e --- /dev/null +++ b/dip-template/pallets/pallet-postit/src/lib.rs @@ -0,0 +1,185 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 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)] + +pub mod post; +pub mod traits; + +pub use pallet::*; + +#[frame_support::pallet(dev_mode)] +pub mod pallet { + + use super::*; + + use frame_support::{ + pallet_prelude::{DispatchResult, *}, + traits::EnsureOrigin, + BoundedVec, + }; + use frame_system::pallet_prelude::*; + use sp_runtime::traits::Hash; + use sp_std::fmt::Debug; + + use crate::{ + post::{Comment, Post}, + traits::Usernamable, + }; + + const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + + pub type BoundedTextOf = BoundedVec::MaxTextLength>; + pub type PostOf = Post<::Hash, BoundedTextOf, ::Username>; + pub type CommentOf = Comment<::Hash, BoundedTextOf, ::Username>; + + #[pallet::config] + pub trait Config: frame_system::Config { + type MaxTextLength: Get; + type OriginCheck: EnsureOrigin<::RuntimeOrigin, Success = Self::OriginSuccess>; + type OriginSuccess: Usernamable; + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type Username: Encode + Decode + TypeInfo + MaxEncodedLen + Clone + PartialEq + Debug + Default; + } + + #[pallet::storage] + #[pallet::getter(fn posts)] + pub type Posts = StorageMap<_, Twox64Concat, ::Hash, PostOf>; + + #[pallet::storage] + #[pallet::getter(fn comments)] + pub type Comments = StorageMap<_, Twox64Concat, ::Hash, CommentOf>; + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(_); + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + NewPost { + post_id: T::Hash, + author: T::Username, + }, + NewComment { + resource_id: T::Hash, + comment_id: T::Hash, + author: T::Username, + }, + NewLike { + resource_id: T::Hash, + liker: T::Username, + }, + } + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(1_000)] + pub fn post(origin: OriginFor, text: BoundedTextOf) -> DispatchResult { + let success_origin = T::OriginCheck::ensure_origin(origin)?; + let author = success_origin.username().map_err(DispatchError::Other)?; + let post_id = T::Hashing::hash( + (&frame_system::Pallet::::block_number(), &author, &text) + .encode() + .as_slice(), + ); + let post = PostOf::::from_text_and_author(text, author.clone()); + Posts::::insert(post_id, post); + Self::deposit_event(Event::NewPost { post_id, author }); + Ok(()) + } + + #[pallet::call_index(1)] + #[pallet::weight(1_000)] + pub fn comment(origin: OriginFor, resource_id: T::Hash, text: BoundedTextOf) -> DispatchResult { + let success_origin = T::OriginCheck::ensure_origin(origin)?; + let author = success_origin.username().map_err(DispatchError::Other)?; + let comment_id = T::Hashing::hash( + (&frame_system::Pallet::::block_number(), &author, &text) + .encode() + .as_slice(), + ); + Posts::::try_mutate(resource_id, |post| { + if let Some(post) = post { + post.comments + .try_push(comment_id) + .expect("Failed to add comment to post."); + Ok(()) + } else { + Err(()) + } + }) + .or_else(|_| { + Comments::::try_mutate(resource_id, |comment| { + if let Some(comment) = comment { + comment + .details + .comments + .try_push(comment_id) + .expect("Failed to add comment to comment."); + Ok(()) + } else { + Err(()) + } + }) + }) + .map_err(|_| DispatchError::Other("No post or comment with provided ID found."))?; + let comment = CommentOf::::from_post_id_text_and_author(resource_id, text, author.clone()); + Comments::::insert(comment_id, comment); + Self::deposit_event(Event::NewComment { + resource_id, + comment_id, + author, + }); + Ok(()) + } + + #[pallet::call_index(2)] + #[pallet::weight(1_000)] + pub fn like(origin: OriginFor, resource_id: T::Hash) -> DispatchResult { + let success_origin = T::OriginCheck::ensure_origin(origin)?; + let liker = success_origin.username().map_err(DispatchError::Other)?; + Posts::::try_mutate(resource_id, |post| { + if let Some(post) = post { + post.likes.try_push(liker.clone()).expect("Failed to add like to post."); + Ok(()) + } else { + Err(()) + } + }) + .or_else(|_| { + Comments::::try_mutate(resource_id, |comment| { + if let Some(comment) = comment { + comment + .details + .likes + .try_push(liker.clone()) + .expect("Failed to add like to comment."); + Ok(()) + } else { + Err(()) + } + }) + }) + .map_err(|_| DispatchError::Other("No post or comment with provided ID found."))?; + Self::deposit_event(Event::NewLike { resource_id, liker }); + Ok(()) + } + } +} diff --git a/dip-template/pallets/pallet-postit/src/post.rs b/dip-template/pallets/pallet-postit/src/post.rs new file mode 100644 index 0000000000..49fd158fc9 --- /dev/null +++ b/dip-template/pallets/pallet-postit/src/post.rs @@ -0,0 +1,55 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 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 frame_support::{traits::ConstU32, BoundedVec}; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; + +#[derive(Encode, Decode, TypeInfo, MaxEncodedLen, Default)] +pub struct Post { + pub author: Username, + pub text: Text, + pub likes: BoundedVec>, + pub comments: BoundedVec>, +} + +impl Post { + pub(crate) fn from_text_and_author(text: Text, author: Username) -> Self { + Self { + text, + author, + likes: BoundedVec::default(), + comments: BoundedVec::default(), + } + } +} + +#[derive(Encode, Decode, TypeInfo, MaxEncodedLen, Default)] +pub struct Comment { + pub details: Post, + pub in_response_to: Id, +} + +impl Comment { + pub(crate) fn from_post_id_text_and_author(in_response_to: Id, text: Text, author: Username) -> Self { + Self { + in_response_to, + details: Post::from_text_and_author(text, author), + } + } +} diff --git a/dip-template/pallets/pallet-postit/src/traits.rs b/dip-template/pallets/pallet-postit/src/traits.rs new file mode 100644 index 0000000000..8a1d20f580 --- /dev/null +++ b/dip-template/pallets/pallet-postit/src/traits.rs @@ -0,0 +1,23 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 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 + +pub trait Usernamable { + type Username; + + fn username(&self) -> Result; +} diff --git a/dip-template/runtimes/dip-consumer/Cargo.toml b/dip-template/runtimes/dip-consumer/Cargo.toml index b5a18965ee..aae167b098 100644 --- a/dip-template/runtimes/dip-consumer/Cargo.toml +++ b/dip-template/runtimes/dip-consumer/Cargo.toml @@ -19,11 +19,12 @@ scale-info = {workspace = true, features = ["derive"]} # DIP dip-provider-runtime-template.workspace = true -dip-support.workspace = true did.workspace = true kilt-dip-support.workspace = true pallet-did-lookup.workspace = true pallet-dip-consumer.workspace = true +pallet-postit.workspace = true +pallet-relay-store.workspace = true runtime-common.workspace = true # Substrate @@ -53,18 +54,10 @@ sp-std.workspace = true sp-transaction-pool.workspace = true sp-version.workspace = true -# Polkadot -pallet-xcm.workspace = true -xcm.workspace = true -xcm-builder.workspace = true -xcm-executor.workspace = true - # Cumulus cumulus-pallet-aura-ext.workspace = true cumulus-pallet-dmp-queue.workspace = true cumulus-pallet-parachain-system.workspace = true -cumulus-pallet-xcm.workspace = true -cumulus-pallet-xcmp-queue.workspace = true cumulus-primitives-core.workspace = true cumulus-primitives-timestamp.workspace = true cumulus-primitives-utility.workspace = true @@ -79,11 +72,12 @@ std = [ "parity-scale-codec/std", "scale-info/std", "dip-provider-runtime-template/std", - "dip-support/std", "did/std", "kilt-dip-support/std", "pallet-did-lookup/std", "pallet-dip-consumer/std", + "pallet-postit/std", + "pallet-relay-store/std", "runtime-common/std", "frame-executive/std", "frame-support/std", @@ -110,15 +104,9 @@ std = [ "sp-std/std", "sp-transaction-pool/std", "sp-version/std", - "pallet-xcm/std", - "xcm/std", - "xcm-builder/std", - "xcm-executor/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-dmp-queue/std", "cumulus-pallet-parachain-system/std", - "cumulus-pallet-xcm/std", - "cumulus-pallet-xcmp-queue/std", "cumulus-primitives-core/std", "cumulus-primitives-timestamp/std", "cumulus-primitives-utility/std", @@ -128,10 +116,9 @@ std = [ runtime-benchmarks = [ "dip-provider-runtime-template/runtime-benchmarks", + "kilt-dip-support/runtime-benchmarks", "pallet-dip-consumer/runtime-benchmarks", "runtime-common/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", - "pallet-xcm/runtime-benchmarks", - "xcm-builder/runtime-benchmarks", ] diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index b629fe5820..11b355dd7f 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -17,54 +17,45 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use did::{did_details::DidVerificationKey, DidVerificationKeyRelationship, KeyIdOf}; -use dip_provider_runtime_template::Web3Name; +use dip_provider_runtime_template::{Runtime as ProviderRuntime, Web3Name}; use frame_support::traits::Contains; use kilt_dip_support::{ - did::{DidSignatureAndCallVerifier, MerkleLeavesAndDidSignature, MerkleRevealedDidSignatureVerifier}, - merkle::{DidMerkleProofVerifier, MerkleProof, ProofLeaf}, - traits::{BlockNumberProvider, DidDipOriginFilter, GenesisProvider}, - MerkleProofAndDidSignatureVerifier, + traits::{DipCallOriginFilter, FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet}, + DipSiblingProviderStateProofVerifier, RococoStateRootsViaRelayStorePallet, }; use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_dip_consumer::traits::IdentityProofVerifier; -use sp_std::vec::Vec; +use sp_core::ConstU32; +use sp_runtime::traits::BlakeTwo256; -use crate::{AccountId, BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; +use crate::{AccountId, DidIdentifier, Runtime, RuntimeCall, RuntimeOrigin}; -pub type MerkleProofVerifier = - DidMerkleProofVerifier, BlockNumber, u128, Web3Name, LinkableAccountId, 10, 10>; pub type MerkleProofVerifierOutputOf = - >::VerificationResult; -pub type MerkleDidSignatureVerifierOf = MerkleRevealedDidSignatureVerifier< - KeyIdOf, - BlockNumber, - Hash, - u128, + >::VerificationResult; +pub type ProofVerifier = DipSiblingProviderStateProofVerifier< + RococoStateRootsViaRelayStorePallet, + ConstU32<2_000>, + ProviderParachainStateInfoViaProviderPallet, AccountId, - MerkleProofVerifierOutputOf, - BlockNumberProvider, + BlakeTwo256, + KeyIdOf, + Web3Name, + LinkableAccountId, + 10, + 10, + u128, // Signatures are valid for 50 blocks - 50, - GenesisProvider, - Hash, + FrameSystemDidSignatureContext, + DipCallFilter, >; impl pallet_dip_consumer::Config for Runtime { type DipCallOriginFilter = PreliminaryDipOriginFilter; type Identifier = DidIdentifier; - type IdentityDetails = u128; - type Proof = MerkleLeavesAndDidSignature< - MerkleProof>, ProofLeaf>, - BlockNumber, - >; - type ProofDigest = Hash; - type ProofVerifier = MerkleProofAndDidSignatureVerifier< - BlockNumber, - MerkleProofVerifier, - DidSignatureAndCallVerifier, DipCallFilter>, - >; + type IdentityProof = >::Proof; + type LocalIdentityInfo = u128; + type ProofVerifier = ProofVerifier; type RuntimeCall = RuntimeCall; - type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; } @@ -74,7 +65,7 @@ impl Contains for PreliminaryDipOriginFilter { fn contains(t: &RuntimeCall) -> bool { matches!( t, - RuntimeCall::DidLookup { .. } + RuntimeCall::PostIt { .. } | RuntimeCall::Utility(pallet_utility::Call::batch { .. }) | RuntimeCall::Utility(pallet_utility::Call::batch_all { .. }) | RuntimeCall::Utility(pallet_utility::Call::force_batch { .. }) @@ -84,7 +75,7 @@ impl Contains for PreliminaryDipOriginFilter { fn derive_verification_key_relationship(call: &RuntimeCall) -> Option { match call { - RuntimeCall::DidLookup { .. } => Some(DidVerificationKeyRelationship::Authentication), + RuntimeCall::PostIt { .. } => Some(DidVerificationKeyRelationship::Authentication), RuntimeCall::Utility(pallet_utility::Call::batch { calls }) => single_key_relationship(calls.iter()).ok(), RuntimeCall::Utility(pallet_utility::Call::batch_all { calls }) => single_key_relationship(calls.iter()).ok(), RuntimeCall::Utility(pallet_utility::Call::force_batch { calls }) => single_key_relationship(calls.iter()).ok(), @@ -116,7 +107,7 @@ fn single_key_relationship<'a>( pub struct DipCallFilter; -impl DidDipOriginFilter for DipCallFilter { +impl DipCallOriginFilter for DipCallFilter { type Error = (); type OriginInfo = (DidVerificationKey, DidVerificationKeyRelationship); type Success = (); @@ -132,43 +123,6 @@ impl DidDipOriginFilter for DipCallFilter { } } -#[cfg(test)] -mod dip_call_origin_filter_tests { - use super::*; - - use frame_support::assert_err; - - #[test] - fn test_key_relationship_derivation() { - // Can call DidLookup functions with an authentication key - let did_lookup_call = RuntimeCall::DidLookup(pallet_did_lookup::Call::associate_sender {}); - assert_eq!( - single_key_relationship(vec![did_lookup_call].iter()), - Ok(DidVerificationKeyRelationship::Authentication) - ); - // Can't call System functions with a DID key (hence a DIP origin) - let system_call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); - assert_err!(single_key_relationship(vec![system_call].iter()), ()); - // Can't call empty batch with a DID key - let empty_batch_call = RuntimeCall::Utility(pallet_utility::Call::batch_all { calls: vec![] }); - assert_err!(single_key_relationship(vec![empty_batch_call].iter()), ()); - // Can call batch with a DipLookup with an authentication key - let did_lookup_batch_call = RuntimeCall::Utility(pallet_utility::Call::batch_all { - calls: vec![pallet_did_lookup::Call::associate_sender {}.into()], - }); - assert_eq!( - single_key_relationship(vec![did_lookup_batch_call].iter()), - Ok(DidVerificationKeyRelationship::Authentication) - ); - // Can't call a batch with different required keys - let did_lookup_batch_call = RuntimeCall::Utility(pallet_utility::Call::batch_all { - calls: vec![ - // Authentication key - pallet_did_lookup::Call::associate_sender {}.into(), - // No key - frame_system::Call::remark { remark: vec![] }.into(), - ], - }); - assert_err!(single_key_relationship(vec![did_lookup_batch_call].iter()), ()); - } +impl pallet_relay_store::Config for Runtime { + type MaxRelayBlocksStored = ConstU32<100>; } diff --git a/dip-template/runtimes/dip-consumer/src/lib.rs b/dip-template/runtimes/dip-consumer/src/lib.rs index b68617f36a..5a0c38bbd7 100644 --- a/dip-template/runtimes/dip-consumer/src/lib.rs +++ b/dip-template/runtimes/dip-consumer/src/lib.rs @@ -22,10 +22,7 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); -use did::KeyIdOf; use dip_provider_runtime_template::Web3Name; -use kilt_dip_support::merkle::VerificationResult; -use pallet_did_lookup::linkable_account::LinkableAccountId; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::{MultiAddress, Perbill, Permill}; @@ -51,7 +48,6 @@ use frame_system::{ }; use pallet_balances::AccountData; use pallet_collator_selection::IdentityCollator; -use pallet_dip_consumer::{DipOrigin, EnsureDipOrigin}; use pallet_session::{FindAccountFromAuthorIndex, PeriodicSessions}; use pallet_transaction_payment::{CurrencyAdapter, FeeDetails, RuntimeDispatchInfo}; use sp_api::impl_runtime_apis; @@ -68,8 +64,8 @@ use sp_std::{prelude::*, time::Duration}; use sp_version::RuntimeVersion; mod dip; -mod xcm_config; -pub use crate::{dip::*, xcm_config::*}; +mod origin_adapter; +pub use crate::{dip::*, origin_adapter::*}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -136,17 +132,12 @@ construct_runtime!( Aura: pallet_aura = 23, AuraExt: cumulus_pallet_aura_ext = 24, - // XCM - XcmpQueue: cumulus_pallet_xcmp_queue = 30, - DmpQueue: cumulus_pallet_dmp_queue = 31, - PolkadotXcm: pallet_xcm = 32, - CumulusXcm: cumulus_pallet_xcm = 33, - - // DID lookup - DidLookup: pallet_did_lookup = 40, + // PostIt + PostIt: pallet_postit = 30, // DIP - DipConsumer: pallet_dip_consumer = 50, + DipConsumer: pallet_dip_consumer = 40, + RelayStore: pallet_relay_store = 41, } ); @@ -251,21 +242,16 @@ impl frame_system::Config for Runtime { type Version = Version; } -parameter_types! { - pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); - pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); -} - impl cumulus_pallet_parachain_system::Config for Runtime { type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; - type DmpMessageHandler = DmpQueue; + type DmpMessageHandler = (); type OnSystemEvent = (); - type OutboundXcmpMessageSource = XcmpQueue; - type ReservedDmpWeight = ReservedDmpWeight; - type ReservedXcmpWeight = ReservedXcmpWeight; + type OutboundXcmpMessageSource = (); + type ReservedDmpWeight = (); + type ReservedXcmpWeight = (); type RuntimeEvent = RuntimeEvent; type SelfParaId = ParachainInfo; - type XcmpMessageHandler = XcmpQueue; + type XcmpMessageHandler = (); } impl pallet_timestamp::Config for Runtime { @@ -367,27 +353,12 @@ impl pallet_aura::Config for Runtime { impl cumulus_pallet_aura_ext::Config for Runtime {} -parameter_types! { - pub const LinkDeposit: Balance = UNIT; -} - -impl pallet_did_lookup::Config for Runtime { - type Currency = Balances; - type Deposit = ConstU128; - type DidIdentifier = DidIdentifier; - type EnsureOrigin = EnsureDipOrigin< - DidIdentifier, - AccountId, - VerificationResult, BlockNumber, Web3Name, LinkableAccountId, 10, 10>, - >; - type OriginSuccess = DipOrigin< - DidIdentifier, - AccountId, - VerificationResult, BlockNumber, Web3Name, LinkableAccountId, 10, 10>, - >; +impl pallet_postit::Config for Runtime { + type MaxTextLength = ConstU32<160>; + type OriginCheck = EnsureDipOriginAdapter; + type OriginSuccess = DipOriginAdapter; type RuntimeEvent = RuntimeEvent; - type RuntimeHoldReason = RuntimeHoldReason; - type WeightInfo = (); + type Username = Web3Name; } impl_runtime_apis! { diff --git a/dip-template/runtimes/dip-consumer/src/origin_adapter.rs b/dip-template/runtimes/dip-consumer/src/origin_adapter.rs new file mode 100644 index 0000000000..b9d008e5c8 --- /dev/null +++ b/dip-template/runtimes/dip-consumer/src/origin_adapter.rs @@ -0,0 +1,59 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 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 crate::{AccountId, DidIdentifier, MerkleProofVerifierOutputOf, RuntimeCall, RuntimeOrigin, Web3Name}; +use frame_support::traits::EnsureOrigin; +use pallet_dip_consumer::{DipOrigin, EnsureDipOrigin}; +use pallet_postit::traits::Usernamable; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_core::RuntimeDebug; + +pub struct EnsureDipOriginAdapter; + +impl EnsureOrigin for EnsureDipOriginAdapter { + type Success = DipOriginAdapter; + + fn try_origin(o: RuntimeOrigin) -> Result { + EnsureDipOrigin::try_origin(o).map(DipOriginAdapter) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + // TODO: Replace with actual DIP origin upon benchmarking + Ok(RuntimeOrigin::root()) + } +} + +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +pub struct DipOriginAdapter( + DipOrigin>, +); + +impl Usernamable for DipOriginAdapter { + type Username = Web3Name; + + fn username(&self) -> Result { + self.0 + .details + .web3_name + .as_ref() + .map(|leaf| leaf.web3_name.clone()) + .ok_or("No username for the subject.") + } +} diff --git a/dip-template/runtimes/dip-consumer/src/xcm_config.rs b/dip-template/runtimes/dip-consumer/src/xcm_config.rs deleted file mode 100644 index 24443a088e..0000000000 --- a/dip-template/runtimes/dip-consumer/src/xcm_config.rs +++ /dev/null @@ -1,153 +0,0 @@ -// KILT Blockchain – https://botlabs.org -// Copyright (C) 2019-2023 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 cumulus_primitives_utility::ParentAsUmp; -use frame_support::{ - parameter_types, - traits::{ConstU32, Contains, Nothing}, - weights::{IdentityFee, Weight}, -}; -use frame_system::EnsureRoot; -use kilt_dip_support::xcm::{barriers::OkOrElseCheckForParachainProvider, origins::AccountIdJunctionAsParachain}; -use pallet_xcm::TestWeightInfo; -use xcm::v3::prelude::*; -use xcm_builder::{ - Account32Hash, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, - SignedToAccountId32, UsingComponents, -}; -use xcm_executor::XcmExecutor; - -use crate::{ - AccountId, AllPalletsWithSystem, Balance, Balances, ParachainInfo, ParachainSystem, Runtime, RuntimeCall, - RuntimeEvent, RuntimeOrigin, XcmpQueue, -}; - -parameter_types! { - pub HereLocation: MultiLocation = MultiLocation::here(); - pub NoneNetworkId: Option = None; - pub UnitWeightCost: Weight = Weight::from_parts(1_000, 0); - pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); -} - -pub type Barrier = OkOrElseCheckForParachainProvider, ConstU32<2_000>>; -pub type AssetTransactorLocationConverter = Account32Hash; -pub type LocalAssetTransactor = - CurrencyAdapter, AssetTransactorLocationConverter, AccountId, ()>; -pub type XcmRouter = (ParentAsUmp, XcmpQueue); - -pub struct DipTransactSafeCalls; - -impl Contains for DipTransactSafeCalls { - fn contains(t: &RuntimeCall) -> bool { - matches!( - t, - RuntimeCall::DipConsumer(pallet_dip_consumer::Call::process_identity_action { .. }) - ) - } -} - -pub struct XcmConfig; -impl xcm_executor::Config for XcmConfig { - type AssetClaims = (); - type AssetExchanger = (); - type AssetLocker = (); - type AssetTransactor = LocalAssetTransactor; - type AssetTrap = (); - type Barrier = Barrier; - type CallDispatcher = RuntimeCall; - type FeeManager = (); - type IsReserve = (); - type IsTeleporter = (); - type MaxAssetsIntoHolding = ConstU32<64>; - type MessageExporter = (); - type OriginConverter = AccountIdJunctionAsParachain, cumulus_pallet_xcm::Origin, RuntimeOrigin>; - type PalletInstancesInfo = AllPalletsWithSystem; - type ResponseHandler = (); - type RuntimeCall = RuntimeCall; - type SafeCallFilter = DipTransactSafeCalls; - type SubscriptionService = (); - type UniversalAliases = Nothing; - type UniversalLocation = UniversalLocation; - type Trader = UsingComponents, HereLocation, AccountId, Balances, ()>; - type Weigher = FixedWeightBounds>; - type XcmSender = XcmRouter; -} - -impl cumulus_pallet_xcmp_queue::Config for Runtime { - type ChannelInfo = ParachainSystem; - type ControllerOrigin = EnsureRoot; - type ControllerOriginConverter = (); - type ExecuteOverweightOrigin = EnsureRoot; - type PriceForSiblingDelivery = (); - type RuntimeEvent = RuntimeEvent; - type VersionWrapper = (); - type WeightInfo = (); - type XcmExecutor = XcmExecutor; -} - -impl cumulus_pallet_dmp_queue::Config for Runtime { - type ExecuteOverweightOrigin = EnsureRoot; - type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor; -} - -const MAX_INSTRUCTIONS: u32 = 100; - -parameter_types! { - pub RelayNetwork: Option = None; -} -#[cfg(feature = "runtime-benchmarks")] -parameter_types! { - pub ReachableDest: Option = Some(Parent.into()); -} - -pub type XcmPalletToRemoteLocationConverter = SignedToAccountId32; - -impl pallet_xcm::Config for Runtime { - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - - type AdminOrigin = EnsureRoot; - type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; - type Currency = Balances; - type CurrencyMatcher = (); - type ExecuteXcmOrigin = EnsureXcmOrigin; - type MaxLockers = ConstU32<8>; - type MaxRemoteLockConsumers = ConstU32<8>; - type RemoteLockConsumerIdentifier = [u8; 8]; - type RuntimeCall = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type RuntimeOrigin = RuntimeOrigin; - type SendXcmOrigin = EnsureXcmOrigin; - type SovereignAccountOf = (); - type TrustedLockers = (); - type UniversalLocation = UniversalLocation; - type Weigher = FixedWeightBounds>; - type WeightInfo = TestWeightInfo; - type XcmExecuteFilter = Nothing; - type XcmExecutor = XcmExecutor; - type XcmReserveTransferFilter = Nothing; - type XcmRouter = XcmRouter; - type XcmTeleportFilter = Nothing; - #[cfg(feature = "runtime-benchmarks")] - type ReachableDest = ReachableDest; -} - -impl cumulus_pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor; -} diff --git a/dip-template/runtimes/dip-provider/Cargo.toml b/dip-template/runtimes/dip-provider/Cargo.toml index 14fdeb6257..dff25abda3 100644 --- a/dip-template/runtimes/dip-provider/Cargo.toml +++ b/dip-template/runtimes/dip-provider/Cargo.toml @@ -19,7 +19,6 @@ scale-info = {workspace = true, features = ["derive"]} # DIP did.workspace = true -dip-support.workspace = true kilt-dip-support.workspace = true kilt-runtime-api-dip-provider.workspace = true pallet-did-lookup.workspace = true @@ -52,18 +51,10 @@ sp-std.workspace = true sp-transaction-pool.workspace = true sp-version.workspace = true -# Polkadot -pallet-xcm.workspace = true -xcm.workspace = true -xcm-builder.workspace = true -xcm-executor.workspace = true - # Cumulus cumulus-pallet-aura-ext.workspace = true cumulus-pallet-dmp-queue.workspace = true cumulus-pallet-parachain-system.workspace = true -cumulus-pallet-xcm.workspace = true -cumulus-pallet-xcmp-queue.workspace = true cumulus-primitives-core.workspace = true cumulus-primitives-timestamp.workspace = true cumulus-primitives-utility.workspace = true @@ -78,7 +69,6 @@ std = [ "parity-scale-codec/std", "scale-info/std", "did/std", - "dip-support/std", "kilt-dip-support/std", "kilt-runtime-api-dip-provider/std", "pallet-did-lookup/std", @@ -108,15 +98,9 @@ std = [ "sp-std/std", "sp-transaction-pool/std", "sp-version/std", - "pallet-xcm/std", - "xcm/std", - "xcm-builder/std", - "xcm-executor/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-dmp-queue/std", "cumulus-pallet-parachain-system/std", - "cumulus-pallet-xcm/std", - "cumulus-pallet-xcmp-queue/std", "cumulus-primitives-core/std", "cumulus-primitives-timestamp/std", "cumulus-primitives-utility/std", @@ -131,7 +115,5 @@ runtime-benchmarks = [ "pallet-web3-names/runtime-benchmarks", "runtime-common/runtime-benchmarks", "frame-system/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "pallet-xcm/runtime-benchmarks", - "xcm-builder/runtime-benchmarks" + "frame-support/runtime-benchmarks" ] diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index 59b6aedade..108bcbd7f2 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -17,51 +17,16 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use did::{DidRawOrigin, EnsureDidOrigin}; -use dip_support::IdentityDetailsAction; -use kilt_dip_support::xcm::XcmRouterIdentityDispatcher; -use pallet_dip_provider::traits::TxBuilder; -use parity_scale_codec::{Decode, Encode}; use runtime_common::dip::{did::LinkedDidInfoProviderOf, merkle::DidMerkleRootGenerator}; -use xcm::{latest::MultiLocation, DoubleEncoded}; -use crate::{AccountId, DidIdentifier, Hash, Runtime, RuntimeEvent, UniversalLocation, XcmRouter}; - -#[derive(Encode, Decode)] -enum ConsumerParachainCalls { - #[codec(index = 50)] - DipConsumer(ConsumerParachainDipConsumerCalls), -} - -#[derive(Encode, Decode)] -enum ConsumerParachainDipConsumerCalls { - #[codec(index = 0)] - ProcessIdentityAction(IdentityDetailsAction), -} - -pub struct ConsumerParachainTxBuilder; -impl TxBuilder for ConsumerParachainTxBuilder { - type Error = (); - - fn build( - _dest: MultiLocation, - action: IdentityDetailsAction, - ) -> Result, Self::Error> { - let double_encoded: DoubleEncoded<()> = - ConsumerParachainCalls::DipConsumer(ConsumerParachainDipConsumerCalls::ProcessIdentityAction(action)) - .encode() - .into(); - Ok(double_encoded) - } -} +use crate::{AccountId, DidIdentifier, Hash, Runtime, RuntimeEvent}; impl pallet_dip_provider::Config for Runtime { type CommitOriginCheck = EnsureDidOrigin; type CommitOrigin = DidRawOrigin; type Identifier = DidIdentifier; - type IdentityProofDispatcher = XcmRouterIdentityDispatcher; - type IdentityProofGenerator = DidMerkleRootGenerator; + type IdentityCommitment = Hash; + type IdentityCommitmentGenerator = DidMerkleRootGenerator; type IdentityProvider = LinkedDidInfoProviderOf; - type ProofOutput = Hash; type RuntimeEvent = RuntimeEvent; - type TxBuilder = ConsumerParachainTxBuilder; } diff --git a/dip-template/runtimes/dip-provider/src/lib.rs b/dip-template/runtimes/dip-provider/src/lib.rs index 989dda86f2..f8b97a98ec 100644 --- a/dip-template/runtimes/dip-provider/src/lib.rs +++ b/dip-template/runtimes/dip-provider/src/lib.rs @@ -76,8 +76,7 @@ pub use sp_runtime::BuildStorage; use sp_version::NativeVersion; mod dip; -mod xcm_config; -pub use crate::{dip::*, xcm_config::*}; +pub use crate::dip::*; pub type AccountId = AccountId32; pub type Address = MultiAddress; @@ -136,19 +135,13 @@ construct_runtime!( Aura: pallet_aura = 23, AuraExt: cumulus_pallet_aura_ext = 24, - // XCM - XcmpQueue: cumulus_pallet_xcmp_queue = 30, - DmpQueue: cumulus_pallet_dmp_queue = 31, - PolkadotXcm: pallet_xcm = 32, - CumulusXcm: cumulus_pallet_xcm = 33, - // DID - Did: did = 40, - DidLookup: pallet_did_lookup = 41, - Web3Names: pallet_web3_names = 42, + Did: did = 30, + DidLookup: pallet_did_lookup = 31, + Web3Names: pallet_web3_names = 32, // DIP - DipProvider: pallet_dip_provider = 50, + DipProvider: pallet_dip_provider = 40, } ); @@ -195,7 +188,8 @@ register_validate_block! { CheckInherents = CheckInherents, } -pub const SS58_PREFIX: u16 = 100; +// Same as official KILT prefix. +pub const SS58_PREFIX: u16 = 38; const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(5); const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( @@ -252,21 +246,16 @@ impl frame_system::Config for Runtime { type Version = Version; } -parameter_types! { - pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); - pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); -} - impl cumulus_pallet_parachain_system::Config for Runtime { type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; - type DmpMessageHandler = DmpQueue; + type DmpMessageHandler = (); type OnSystemEvent = (); - type OutboundXcmpMessageSource = XcmpQueue; - type ReservedDmpWeight = ReservedDmpWeight; - type ReservedXcmpWeight = ReservedXcmpWeight; + type OutboundXcmpMessageSource = (); + type ReservedDmpWeight = (); + type ReservedXcmpWeight = (); type RuntimeEvent = RuntimeEvent; type SelfParaId = ParachainInfo; - type XcmpMessageHandler = XcmpQueue; + type XcmpMessageHandler = (); } impl pallet_timestamp::Config for Runtime { diff --git a/dip-template/runtimes/dip-provider/src/xcm_config.rs b/dip-template/runtimes/dip-provider/src/xcm_config.rs deleted file mode 100644 index 41bcc47fc4..0000000000 --- a/dip-template/runtimes/dip-provider/src/xcm_config.rs +++ /dev/null @@ -1,133 +0,0 @@ -// KILT Blockchain – https://botlabs.org -// Copyright (C) 2019-2023 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 cumulus_primitives_utility::ParentAsUmp; -use frame_support::{ - parameter_types, - traits::{ConstU32, Nothing}, - weights::{IdentityFee, Weight}, -}; -use frame_system::EnsureRoot; -use pallet_xcm::TestWeightInfo; -use xcm::v3::prelude::*; -use xcm_builder::{EnsureXcmOrigin, FixedWeightBounds, SignedToAccountId32, UsingComponents}; -use xcm_executor::XcmExecutor; - -use crate::{ - AccountId, AllPalletsWithSystem, Balance, Balances, ParachainInfo, ParachainSystem, Runtime, RuntimeCall, - RuntimeEvent, RuntimeOrigin, XcmpQueue, -}; - -parameter_types! { - pub HereLocation: MultiLocation = Junctions::Here.into(); - pub UnitWeightCost: Weight = Weight::from_parts(1_000, 0); - pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); -} - -pub type XcmRouter = (ParentAsUmp, XcmpQueue); - -pub struct XcmConfig; -impl xcm_executor::Config for XcmConfig { - type AssetClaims = (); - type AssetExchanger = (); - type AssetLocker = (); - type AssetTransactor = (); - type AssetTrap = (); - type Barrier = (); - type CallDispatcher = RuntimeCall; - type FeeManager = (); - type IsReserve = (); - type IsTeleporter = (); - type MaxAssetsIntoHolding = ConstU32<64>; - type MessageExporter = (); - type OriginConverter = (); - type PalletInstancesInfo = AllPalletsWithSystem; - type ResponseHandler = (); - type RuntimeCall = RuntimeCall; - type SafeCallFilter = Nothing; - type SubscriptionService = (); - type UniversalAliases = Nothing; - type UniversalLocation = UniversalLocation; - type Trader = UsingComponents, HereLocation, AccountId, Balances, ()>; - type Weigher = FixedWeightBounds>; - type XcmSender = XcmRouter; -} - -impl cumulus_pallet_xcmp_queue::Config for Runtime { - type ChannelInfo = ParachainSystem; - type ControllerOrigin = EnsureRoot; - type ControllerOriginConverter = (); - type ExecuteOverweightOrigin = EnsureRoot; - type PriceForSiblingDelivery = (); - type RuntimeEvent = RuntimeEvent; - type VersionWrapper = (); - type WeightInfo = (); - type XcmExecutor = XcmExecutor; -} - -impl cumulus_pallet_dmp_queue::Config for Runtime { - type ExecuteOverweightOrigin = EnsureRoot; - type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor; -} - -const MAX_INSTRUCTIONS: u32 = 100; - -parameter_types! { - pub RelayNetwork: Option = None; -} -#[cfg(feature = "runtime-benchmarks")] -parameter_types! { - pub ReachableDest: Option = Some(Parent.into()); -} - -pub type XcmPalletToRemoteLocationConverter = SignedToAccountId32; - -impl pallet_xcm::Config for Runtime { - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - - type AdminOrigin = EnsureRoot; - type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; - type Currency = Balances; - type CurrencyMatcher = (); - type ExecuteXcmOrigin = EnsureXcmOrigin; - type MaxLockers = ConstU32<8>; - type MaxRemoteLockConsumers = ConstU32<8>; - type RemoteLockConsumerIdentifier = [u8; 8]; - type RuntimeCall = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type RuntimeOrigin = RuntimeOrigin; - type SendXcmOrigin = EnsureXcmOrigin; - type SovereignAccountOf = (); - type TrustedLockers = (); - type UniversalLocation = UniversalLocation; - type Weigher = FixedWeightBounds>; - type WeightInfo = TestWeightInfo; - type XcmExecuteFilter = Nothing; - type XcmExecutor = XcmExecutor; - type XcmReserveTransferFilter = Nothing; - type XcmRouter = XcmRouter; - type XcmTeleportFilter = Nothing; - #[cfg(feature = "runtime-benchmarks")] - type ReachableDest = ReachableDest; -} - -impl cumulus_pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor; -} diff --git a/pallets/pallet-dip-consumer/Cargo.toml b/pallets/pallet-dip-consumer/Cargo.toml index a59f1e8f9d..5025ad90dd 100644 --- a/pallets/pallet-dip-consumer/Cargo.toml +++ b/pallets/pallet-dip-consumer/Cargo.toml @@ -14,25 +14,23 @@ version.workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -cumulus-pallet-xcm.workspace = true -dip-support.workspace = true frame-support.workspace = true frame-system.workspace = true kilt-support.workspace = true parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} +sp-core.workspace = true sp-std.workspace = true [features] default = ["std"] std = [ - "cumulus-pallet-xcm/std", - "dip-support/std", "frame-support/std", "frame-system/std", "kilt-support/std", "parity-scale-codec/std", "scale-info/std", + "sp-core/std", "sp-std/std", ] runtime-benchmarks = [ diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index ab0cde6dda..cb0e49f801 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -31,15 +31,13 @@ pub use crate::{origin::*, pallet::*}; pub mod pallet { use super::*; - use cumulus_pallet_xcm::ensure_sibling_para; use frame_support::{dispatch::Dispatchable, pallet_prelude::*, traits::Contains, Twox64Concat}; use frame_system::pallet_prelude::*; - use parity_scale_codec::MaxEncodedLen; + use parity_scale_codec::{FullCodec, MaxEncodedLen}; + use scale_info::TypeInfo; use sp_std::boxed::Box; - use dip_support::IdentityDetailsAction; - - use crate::{identity::IdentityDetails, traits::IdentityProofVerifier}; + use crate::traits::IdentityProofVerifier; pub type VerificationResultOf = <::ProofVerifier as IdentityProofVerifier< ::RuntimeCall, @@ -48,15 +46,10 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); - // TODO: Store also additional details received by the provider. #[pallet::storage] #[pallet::getter(fn identity_proofs)] - pub(crate) type IdentityEntries = StorageMap< - _, - Twox64Concat, - ::Identifier, - IdentityDetails<::ProofDigest, ::IdentityDetails>, - >; + pub(crate) type IdentityEntries = + StorageMap<_, Twox64Concat, ::Identifier, ::LocalIdentityInfo>; #[pallet::config] pub trait Config: frame_system::Config { @@ -65,51 +58,33 @@ pub mod pallet { type DipCallOriginFilter: Contains<::RuntimeCall>; /// The identifier of a subject, e.g., a DID. type Identifier: Parameter + MaxEncodedLen; - /// The details stored in this pallet associated with any given subject. - type IdentityDetails: Parameter + MaxEncodedLen + Default; /// The proof users must provide to operate with their higher-level /// identity. Depending on the use cases, this proof can contain /// heterogeneous bits of information that the proof verifier will /// utilize. For instance, a proof could contain both a Merkle proof and /// a DID signature. - type Proof: Parameter; - /// The type of the committed proof digest used as the basis for - /// verifying identity proofs. - type ProofDigest: Parameter + MaxEncodedLen; + type IdentityProof: Parameter; + /// The details stored in this pallet associated with any given subject. + type LocalIdentityInfo: FullCodec + TypeInfo + MaxEncodedLen; /// The logic of the proof verifier, called upon each execution of the /// `dispatch_as` extrinsic. type ProofVerifier: IdentityProofVerifier< ::RuntimeCall, Self::Identifier, - Proof = Self::Proof, - IdentityDetails = IdentityDetails, + Proof = Self::IdentityProof, + IdentityDetails = Self::LocalIdentityInfo, Submitter = ::AccountId, >; /// The overarching runtime call type. type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin>; - /// The overarching event type. - type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// The overarching runtime origin type. - type RuntimeOrigin: From> - + From<::RuntimeOrigin> - + Into::RuntimeOrigin>>; + type RuntimeOrigin: From> + From<::RuntimeOrigin>; } #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// The identity information related to a given subject has been - /// deleted. - IdentityInfoDeleted(T::Identifier), - /// The identity information related to a given subject has been updated - /// to a new digest. - IdentityInfoUpdated(T::Identifier, T::ProofDigest), - } - #[pallet::error] pub enum Error { /// An identity with the provided identifier could not be found. @@ -129,59 +104,34 @@ pub mod pallet { // TODO: Benchmarking #[pallet::call] impl Pallet { - #[pallet::call_index(0)] - #[pallet::weight(0)] - pub fn process_identity_action( - origin: OriginFor, - action: IdentityDetailsAction, - ) -> DispatchResult { - ensure_sibling_para(::RuntimeOrigin::from(origin))?; - - let event = match action { - IdentityDetailsAction::Updated(identifier, proof, _) => { - IdentityEntries::::mutate( - &identifier, - |entry: &mut Option< - IdentityDetails<::ProofDigest, ::IdentityDetails>, - >| { *entry = Some(proof.clone().into()) }, - ); - Ok::<_, Error>(Event::::IdentityInfoUpdated(identifier, proof)) - } - IdentityDetailsAction::Deleted(identifier) => { - IdentityEntries::::remove(&identifier); - Ok::<_, Error>(Event::::IdentityInfoDeleted(identifier)) - } - }?; - - Self::deposit_event(event); - - Ok(()) - } - // TODO: Replace with a SignedExtra. - #[pallet::call_index(1)] + #[pallet::call_index(0)] #[pallet::weight(0)] pub fn dispatch_as( origin: OriginFor, identifier: T::Identifier, - proof: T::Proof, + proof: T::IdentityProof, call: Box<::RuntimeCall>, ) -> DispatchResult { + // TODO: Make origin check configurable, and require that it at least returns + // the submitter's account. let submitter = ensure_signed(origin)?; // TODO: Proper error handling ensure!(T::DipCallOriginFilter::contains(&*call), Error::::Dispatch); - let mut identity_entry = IdentityEntries::::get(&identifier).ok_or(Error::::IdentityNotFound)?; + let mut identity_entry = IdentityEntries::::get(&identifier); let proof_verification_result = T::ProofVerifier::verify_proof_for_call_against_details( &*call, &identifier, &submitter, &mut identity_entry, - &proof, - ) - .map_err(|_| Error::::InvalidProof)?; + proof, + ); // Write the identity info to storage after it has optionally been updated by - // the `ProofVerifier`. - IdentityEntries::::mutate(&identifier, |entry| *entry = Some(identity_entry)); + // the `ProofVerifier`, regardless of whether the proof has been verified or + // not. + IdentityEntries::::mutate(&identifier, |entry| *entry = identity_entry); + // Unwrap the result if `ok`. + let proof_verification_result = proof_verification_result.map_err(|_| Error::::InvalidProof)?; let did_origin = DipOrigin { identifier, account_address: submitter, diff --git a/pallets/pallet-dip-consumer/src/traits.rs b/pallets/pallet-dip-consumer/src/traits.rs index 63a5c8a831..0b7fa2a85c 100644 --- a/pallets/pallet-dip-consumer/src/traits.rs +++ b/pallets/pallet-dip-consumer/src/traits.rs @@ -20,8 +20,8 @@ use sp_std::marker::PhantomData; pub trait IdentityProofVerifier { type Error; - type Proof; type IdentityDetails; + type Proof; type Submitter; type VerificationResult; @@ -29,19 +29,19 @@ pub trait IdentityProofVerifier { call: &Call, subject: &Subject, submitter: &Self::Submitter, - identity_details: &mut Self::IdentityDetails, - proof: &Self::Proof, + identity_details: &mut Option, + proof: Self::Proof, ) -> Result; } // Always returns success. -pub struct SuccessfulProofVerifier(PhantomData<(Proof, ProofEntry, Submitter)>); -impl IdentityProofVerifier - for SuccessfulProofVerifier +pub struct SuccessfulProofVerifier(PhantomData<(IdentityDetails, Proof, Submitter)>); +impl IdentityProofVerifier + for SuccessfulProofVerifier { type Error = (); + type IdentityDetails = IdentityDetails; type Proof = Proof; - type IdentityDetails = ProofEntry; type Submitter = Submitter; type VerificationResult = (); @@ -49,8 +49,8 @@ impl IdentityProofVerifier, + _proof: Self::Proof, ) -> Result { Ok(()) } diff --git a/pallets/pallet-dip-provider/Cargo.toml b/pallets/pallet-dip-provider/Cargo.toml index e87da12329..5e22e357a1 100644 --- a/pallets/pallet-dip-provider/Cargo.toml +++ b/pallets/pallet-dip-provider/Cargo.toml @@ -15,25 +15,21 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] did.workspace = true -dip-support.workspace = true frame-support.workspace = true frame-system.workspace = true parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} sp-std.workspace = true -xcm.workspace = true [features] default = ["std"] std = [ "did/std", - "dip-support/std", "frame-support/std", "frame-system/std", "parity-scale-codec/std", "scale-info/std", "sp-std/std", - "xcm/std", ] runtime-benchmarks = [ "did/runtime-benchmarks", diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index 4f136590cd..5171084147 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -28,17 +28,14 @@ pub use crate::pallet::*; pub mod pallet { use super::*; - use frame_support::{pallet_prelude::*, traits::EnsureOrigin, weights::Weight}; + use frame_support::{pallet_prelude::*, traits::EnsureOrigin}; use frame_system::pallet_prelude::*; - use sp_std::{boxed::Box, fmt::Debug}; - use xcm::{latest::prelude::*, VersionedMultiAsset, VersionedMultiLocation}; + use parity_scale_codec::FullCodec; + use sp_std::fmt::Debug; - use dip_support::IdentityDetailsAction; - - use crate::traits::{IdentityProofDispatcher, IdentityProofGenerator, IdentityProvider, SubmitterInfo, TxBuilder}; + use crate::traits::{IdentityCommitmentGenerator, IdentityProvider, SubmitterInfo}; pub type IdentityOf = <::IdentityProvider as IdentityProvider<::Identifier>>::Success; - pub type IdentityProofActionOf = IdentityDetailsAction<::Identifier, ::ProofOutput>; const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); @@ -46,19 +43,22 @@ pub mod pallet { pub trait Config: frame_system::Config { type CommitOriginCheck: EnsureOrigin; type CommitOrigin: SubmitterInfo; - type Identifier: Parameter; - type IdentityProofGenerator: IdentityProofGenerator< + type Identifier: Parameter + MaxEncodedLen; + type IdentityCommitment: Clone + Eq + Debug + TypeInfo + FullCodec + MaxEncodedLen; + type IdentityCommitmentGenerator: IdentityCommitmentGenerator< Self::Identifier, IdentityOf, - Output = Self::ProofOutput, + Output = Self::IdentityCommitment, >; - type IdentityProofDispatcher: IdentityProofDispatcher; type IdentityProvider: IdentityProvider; - type ProofOutput: Clone + Eq + Debug; type RuntimeEvent: From> + IsType<::RuntimeEvent>; - type TxBuilder: TxBuilder; } + #[pallet::storage] + #[pallet::getter(fn identity_commitments)] + pub type IdentityCommitments = + StorageMap<_, Twox64Concat, ::Identifier, ::IdentityCommitment>; + #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); @@ -66,16 +66,19 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - IdentityInfoDispatched(IdentityProofActionOf, Box), + IdentityCommitted { + identifier: T::Identifier, + commitment: T::IdentityCommitment, + }, + IdentityDeleted { + identifier: T::Identifier, + }, } #[pallet::error] pub enum Error { - BadVersion, - Dispatch, IdentityNotFound, - IdentityProofGeneration, - Predispatch, + IdentityCommitmentGeneration, } #[pallet::call] @@ -83,43 +86,29 @@ pub mod pallet { #[pallet::call_index(0)] // TODO: Update weight #[pallet::weight(0)] - pub fn commit_identity( - origin: OriginFor, - identifier: T::Identifier, - destination: Box, - asset: Box, - weight: Weight, - ) -> DispatchResult { - let dispatcher = T::CommitOriginCheck::ensure_origin(origin).map(|e| e.submitter())?; - - let destination: MultiLocation = (*destination).try_into().map_err(|_| Error::::BadVersion)?; - let action: IdentityProofActionOf = match T::IdentityProvider::retrieve(&identifier) { - Ok(Some(identity)) => { - let identity_proof = T::IdentityProofGenerator::generate_commitment(&identifier, &identity) - .map_err(|_| Error::::IdentityProofGeneration)?; - Ok(IdentityDetailsAction::Updated(identifier, identity_proof, ())) - } - Ok(None) => Ok(IdentityDetailsAction::Deleted(identifier)), + pub fn commit_identity(origin: OriginFor, identifier: T::Identifier) -> DispatchResult { + // TODO: use dispatcher to get deposit + let _dispatcher = + T::CommitOriginCheck::ensure_origin(origin).map(|e: ::CommitOrigin| e.submitter())?; + + let identity_commitment: Option = match T::IdentityProvider::retrieve(&identifier) { + Ok(Some(identity)) => T::IdentityCommitmentGenerator::generate_commitment(&identifier, &identity) + .map(Some) + .map_err(|_| Error::::IdentityCommitmentGeneration), + Ok(None) => Ok(None), Err(_) => Err(Error::::IdentityNotFound), }?; - // TODO: Add correct version creation based on lookup (?) - - let asset: MultiAsset = (*asset).try_into().map_err(|_| Error::::BadVersion)?; - - let (ticket, _) = T::IdentityProofDispatcher::pre_dispatch::( - action.clone(), - dispatcher, - asset, - weight, - destination, - ) - .map_err(|_| Error::::Predispatch)?; - // TODO: Use returned asset of `pre_dispatch` to charge the tx submitter for the - // fee. - T::IdentityProofDispatcher::dispatch(ticket).map_err(|_| Error::::Dispatch)?; + if let Some(commitment) = identity_commitment { + // TODO: Take deposit (once 0.9.42 PR is merged into develop) + IdentityCommitments::::insert(&identifier, commitment.clone()); + Self::deposit_event(Event::::IdentityCommitted { identifier, commitment }); + } else { + // TODO: Release deposit (once 0.9.42 PR is merged into develop) + IdentityCommitments::::remove(&identifier); + Self::deposit_event(Event::::IdentityDeleted { identifier }); + } - Self::deposit_event(Event::IdentityInfoDispatched(action, Box::new(destination))); Ok(()) } } diff --git a/pallets/pallet-dip-provider/src/traits.rs b/pallets/pallet-dip-provider/src/traits.rs index f5a8836d1f..5f0d137963 100644 --- a/pallets/pallet-dip-provider/src/traits.rs +++ b/pallets/pallet-dip-provider/src/traits.rs @@ -17,26 +17,24 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use did::DidRawOrigin; -use dip_support::IdentityDetailsAction; -use xcm::{latest::prelude::*, DoubleEncoded}; pub use identity_generation::*; pub mod identity_generation { use sp_std::marker::PhantomData; - pub trait IdentityProofGenerator { + pub trait IdentityCommitmentGenerator { type Error; type Output; fn generate_commitment(identifier: &Identifier, identity: &Identity) -> Result; } - // Implement the `IdentityProofGenerator` by returning the `Default` value for - // the `Output` type. - pub struct DefaultIdentityProofGenerator(PhantomData); + // Implement the `IdentityCommitmentGenerator` by returning the `Default` value + // for the `Output` type. + pub struct DefaultIdentityCommitmentGenerator(PhantomData); - impl IdentityProofGenerator - for DefaultIdentityProofGenerator + impl IdentityCommitmentGenerator + for DefaultIdentityCommitmentGenerator where Output: Default, { @@ -49,52 +47,6 @@ pub mod identity_generation { } } -pub use identity_dispatch::*; -pub mod identity_dispatch { - use super::*; - - use frame_support::weights::Weight; - - pub trait IdentityProofDispatcher { - type PreDispatchOutput; - type Error; - - fn pre_dispatch>( - action: IdentityDetailsAction, - source: AccountId, - asset: MultiAsset, - weight: Weight, - destination: MultiLocation, - ) -> Result<(Self::PreDispatchOutput, MultiAssets), Self::Error>; - - fn dispatch(pre_output: Self::PreDispatchOutput) -> Result<(), Self::Error>; - } - - // Returns `Ok` without doing anything. - pub struct NullIdentityProofDispatcher; - - impl - IdentityProofDispatcher for NullIdentityProofDispatcher - { - type PreDispatchOutput = (); - type Error = (); - - fn pre_dispatch<_B>( - _action: IdentityDetailsAction, - _source: AccountId, - _asset: MultiAsset, - _weight: Weight, - _destination: MultiLocation, - ) -> Result<((), MultiAssets), Self::Error> { - Ok(((), MultiAssets::default())) - } - - fn dispatch(_pre_output: Self::PreDispatchOutput) -> Result<(), Self::Error> { - Ok(()) - } - } -} - pub use identity_provision::*; pub mod identity_provision { use sp_std::marker::PhantomData; @@ -134,17 +86,6 @@ pub mod identity_provision { } } -// Given a destination and an identity action, creates and encodes the proper -// `Transact` call. -pub trait TxBuilder { - type Error; - - fn build( - dest: MultiLocation, - action: IdentityDetailsAction, - ) -> Result, Self::Error>; -} - pub trait SubmitterInfo { type Submitter; diff --git a/pallets/pallet-relay-store/Cargo.toml b/pallets/pallet-relay-store/Cargo.toml new file mode 100644 index 0000000000..c5398f052f --- /dev/null +++ b/pallets/pallet-relay-store/Cargo.toml @@ -0,0 +1,42 @@ +[package] +authors.workspace = true +description = "Pallet enabling storing finalize relay head data on chain." +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +name = "pallet-relay-store" +readme.workspace = true +repository.workspace = true +version.workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +cumulus-pallet-parachain-system.workspace = true +frame-support.workspace = true +frame-system.workspace = true +log.workspace = true +parity-scale-codec = {workspace = true, features = ["derive"]} +scale-info = {workspace = true, features = ["derive"]} +sp-core.workspace = true +sp-runtime.workspace = true +sp-std.workspace = true + +[features] +default = ["std"] +std = [ + "cumulus-pallet-parachain-system/std", + "frame-support/std", + "frame-system/std", + "parity-scale-codec/std", + "scale-info/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", +] diff --git a/pallets/pallet-relay-store/src/lib.rs b/pallets/pallet-relay-store/src/lib.rs new file mode 100644 index 0000000000..3cc307e836 --- /dev/null +++ b/pallets/pallet-relay-store/src/lib.rs @@ -0,0 +1,109 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 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 + +// TODO: Pallet description + +#![cfg_attr(not(feature = "std"), no_std)] + +mod relay; + +pub use crate::pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + use frame_support::{pallet_prelude::*, BoundedVec}; + use frame_system::pallet_prelude::*; + use sp_core::H256; + + use crate::relay::RelayParentInfo; + + const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + + #[pallet::storage] + #[pallet::getter(fn latest_relay_head_for_block)] + pub(crate) type LatestRelayHeads = StorageMap<_, Twox64Concat, u32, RelayParentInfo>; + + // TODO: Replace this with a fixed-length array once support for const generics + // is fully supported in Substrate. + #[pallet::storage] + pub(crate) type LatestBlockHeights = + StorageValue<_, BoundedVec, ValueQuery>; + + #[pallet::config] + pub trait Config: frame_system::Config { + #[pallet::constant] + type MaxRelayBlocksStored: Get; + } + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(_); + + #[pallet::hooks] + impl Hooks> for Pallet + where + T: cumulus_pallet_parachain_system::Config, + { + fn on_initialize(_n: BlockNumberFor) -> Weight { + // Reserve weight to update the last relay state root + // TODO: Replace with benchmarked version of `on_finalize(` + ::DbWeight::get().writes(2) + } + + // TODO: Benchmarks + fn on_finalize(_n: BlockNumberFor) { + // Called before the validation data is cleaned in the + // parachain_system::on_finalize hook + let Some(new_validation_data) = cumulus_pallet_parachain_system::Pallet::::validation_data() else { return; }; + let mut latest_block_heights = LatestBlockHeights::::get(); + // Remove old relay block from both storage entries. + if latest_block_heights.is_full() { + let oldest_block_height = latest_block_heights.remove(0); + LatestRelayHeads::::remove(oldest_block_height); + log::trace!( + "Relay block queue full. Removing oldest block at height {:?}", + oldest_block_height + ); + } + // Set the new relay block in storage. + let relay_block_height = new_validation_data.relay_parent_number; + log::trace!( + "Adding new relay block with state root {:#02x?} and number {:?}", + new_validation_data.relay_parent_storage_root, + new_validation_data.relay_parent_number, + ); + let push_res = latest_block_heights.try_push(relay_block_height); + if let Err(err) = push_res { + log::error!( + "Pushing a new relay block to the queue should not fail but it did when adding relay block n. {:?}", + err + ); + } else { + LatestBlockHeights::::set(latest_block_heights); + LatestRelayHeads::::insert( + relay_block_height, + RelayParentInfo { + relay_parent_storage_root: new_validation_data.relay_parent_storage_root, + }, + ); + } + } + } +} diff --git a/crates/dip-support/src/lib.rs b/pallets/pallet-relay-store/src/relay.rs similarity index 73% rename from crates/dip-support/src/lib.rs rename to pallets/pallet-relay-store/src/relay.rs index 31256a1dda..9dfaf69f95 100644 --- a/crates/dip-support/src/lib.rs +++ b/pallets/pallet-relay-store/src/relay.rs @@ -16,16 +16,11 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -// TODO: Crate documentation - -#![cfg_attr(not(feature = "std"), no_std)] - -use frame_support::RuntimeDebug; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; +use sp_core::RuntimeDebug; -#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen, RuntimeDebug)] -pub enum IdentityDetailsAction { - Updated(Identifier, Proof, Details), - Deleted(Identifier), +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug, MaxEncodedLen)] +pub struct RelayParentInfo { + pub relay_parent_storage_root: Hash, } diff --git a/runtimes/common/Cargo.toml b/runtimes/common/Cargo.toml index da0addb5eb..7134a22ed9 100644 --- a/runtimes/common/Cargo.toml +++ b/runtimes/common/Cargo.toml @@ -69,6 +69,7 @@ runtime-benchmarks = [ "attestation/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "kilt-dip-support/runtime-benchmarks", "kilt-support/runtime-benchmarks", "kilt-dip-support/runtime-benchmarks", "pallet-balances/runtime-benchmarks", diff --git a/runtimes/common/src/dip/did.rs b/runtimes/common/src/dip/did.rs index cebbf104ce..8929a672cb 100644 --- a/runtimes/common/src/dip/did.rs +++ b/runtimes/common/src/dip/did.rs @@ -18,8 +18,8 @@ use did::did_details::DidDetails; use kilt_dip_support::{ - did::{CombineIdentityFrom, CombinedIdentityResult}, merkle::RevealedWeb3Name, + utils::{CombineIdentityFrom, CombinedIdentityResult}, }; use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_dip_provider::traits::IdentityProvider; diff --git a/runtimes/common/src/dip/merkle.rs b/runtimes/common/src/dip/merkle.rs index 254380b65f..62050e2359 100644 --- a/runtimes/common/src/dip/merkle.rs +++ b/runtimes/common/src/dip/merkle.rs @@ -18,22 +18,22 @@ use did::{DidVerificationKeyRelationship, KeyIdOf}; use frame_support::RuntimeDebug; -use kilt_dip_support::merkle::{DidKeyMerkleKey, DidKeyMerkleValue, MerkleProof}; +use kilt_dip_support::merkle::{DidKeyMerkleKey, DidKeyMerkleValue, DidMerkleProof}; use pallet_did_lookup::linkable_account::LinkableAccountId; -use pallet_dip_provider::traits::IdentityProofGenerator; +use pallet_dip_provider::traits::IdentityCommitmentGenerator; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_std::{borrow::ToOwned, marker::PhantomData, vec::Vec}; use sp_trie::{generate_trie_proof, LayoutV1, MemoryDB, TrieDBMutBuilder, TrieHash, TrieMut}; -use kilt_dip_support::merkle::{DidKeyRelationship, ProofLeaf}; +use kilt_dip_support::merkle::{DidKeyRelationship, RevealedDidMerkleProofLeaf}; use crate::{dip::did::LinkedDidInfoOf, DidIdentifier}; pub type BlindedValue = Vec; -pub type DidMerkleProofOf = MerkleProof< +pub type DidMerkleProofOf = DidMerkleProof< Vec, - ProofLeaf< + RevealedDidMerkleProofLeaf< KeyIdOf, ::BlockNumber, ::Web3Name, @@ -49,7 +49,7 @@ pub struct CompleteMerkleProof { pub struct DidMerkleRootGenerator(PhantomData); -type ProofLeafOf = ProofLeaf< +type ProofLeafOf = RevealedDidMerkleProofLeaf< KeyIdOf, ::BlockNumber, ::Web3Name, @@ -212,12 +212,18 @@ where } else { Err(()) }?; - Ok(ProofLeaf::DidKey(did_key_merkle_key, key_details.clone().into())) + Ok(RevealedDidMerkleProofLeaf::DidKey( + did_key_merkle_key, + key_details.clone().into(), + )) }) .chain(account_ids.map(|account_id| -> Result, ()> { let Some(linked_accounts) = linked_accounts else { return Err(()) }; if linked_accounts.contains(account_id) { - Ok(ProofLeaf::LinkedAccount(account_id.clone().into(), ().into())) + Ok(RevealedDidMerkleProofLeaf::LinkedAccount( + account_id.clone().into(), + ().into(), + )) } else { Err(()) } @@ -227,7 +233,7 @@ where match (should_include_web3_name, linked_web3_name) { // If web3name should be included and it exists... (true, Some(web3name_details)) => { - leaves.push(ProofLeaf::Web3Name( + leaves.push(RevealedDidMerkleProofLeaf::Web3Name( web3name_details.web3_name.clone().into(), web3name_details.claimed_at.into(), )); @@ -251,7 +257,7 @@ where } } -impl IdentityProofGenerator> for DidMerkleRootGenerator +impl IdentityCommitmentGenerator> for DidMerkleRootGenerator where T: did::Config + pallet_did_lookup::Config + pallet_web3_names::Config, {