diff --git a/Cargo.lock b/Cargo.lock
index 1ba4cd2766..fc112de481 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2448,7 +2448,6 @@ dependencies = [
"jsonrpsee",
"log",
"pallet-transaction-payment-rpc",
- "parity-scale-codec",
"polkadot-cli",
"polkadot-primitives",
"sc-basic-authorship",
@@ -2486,11 +2485,9 @@ name = "dip-consumer-runtime-template"
version = "1.12.0-dev"
dependencies = [
"cumulus-pallet-aura-ext",
- "cumulus-pallet-dmp-queue",
"cumulus-pallet-parachain-system",
"cumulus-primitives-core",
"cumulus-primitives-timestamp",
- "cumulus-primitives-utility",
"did",
"dip-provider-runtime-template",
"frame-benchmarking",
@@ -2499,13 +2496,11 @@ dependencies = [
"frame-system",
"frame-system-benchmarking",
"frame-system-rpc-runtime-api",
- "hex-literal 0.3.4",
"kilt-dip-primitives",
"pallet-aura",
"pallet-authorship",
"pallet-balances",
"pallet-collator-selection",
- "pallet-did-lookup",
"pallet-dip-consumer",
"pallet-postit",
"pallet-relay-store",
@@ -2517,8 +2512,7 @@ dependencies = [
"pallet-utility",
"parachain-info",
"parity-scale-codec",
- "polkadot-parachain",
- "runtime-common",
+ "rococo-runtime",
"scale-info",
"sp-api",
"sp-block-builder",
@@ -2554,7 +2548,6 @@ dependencies = [
"jsonrpsee",
"log",
"pallet-transaction-payment-rpc",
- "parity-scale-codec",
"polkadot-cli",
"polkadot-primitives",
"sc-basic-authorship",
@@ -2592,11 +2585,9 @@ name = "dip-provider-runtime-template"
version = "1.12.0-dev"
dependencies = [
"cumulus-pallet-aura-ext",
- "cumulus-pallet-dmp-queue",
"cumulus-pallet-parachain-system",
"cumulus-primitives-core",
"cumulus-primitives-timestamp",
- "cumulus-primitives-utility",
"did",
"frame-benchmarking",
"frame-executive",
@@ -2604,11 +2595,8 @@ dependencies = [
"frame-system",
"frame-system-benchmarking",
"frame-system-rpc-runtime-api",
- "hex-literal 0.3.4",
- "kilt-dip-primitives",
"kilt-runtime-api-did",
"kilt-runtime-api-dip-provider",
- "kilt-support",
"log",
"pallet-aura",
"pallet-authorship",
@@ -4614,7 +4602,6 @@ name = "kilt-dip-primitives"
version = "1.12.0-dev"
dependencies = [
"cfg-if",
- "cumulus-pallet-parachain-system",
"cumulus-primitives-core",
"did",
"frame-support",
@@ -4628,9 +4615,7 @@ dependencies = [
"pallet-dip-provider",
"pallet-relay-store",
"pallet-web3-names",
- "parachain-info",
"parity-scale-codec",
- "rococo-runtime",
"scale-info",
"sp-core",
"sp-io",
@@ -4638,8 +4623,6 @@ dependencies = [
"sp-state-machine",
"sp-std",
"sp-trie",
- "xcm",
- "xcm-executor",
]
[[package]]
@@ -6600,7 +6583,6 @@ dependencies = [
"kilt-support",
"parity-scale-codec",
"scale-info",
- "sp-core",
"sp-io",
"sp-keystore",
"sp-runtime",
@@ -6618,7 +6600,6 @@ dependencies = [
"kilt-support",
"parity-scale-codec",
"scale-info",
- "sp-core",
"sp-io",
"sp-keystore",
"sp-runtime",
diff --git a/crates/kilt-dip-primitives/Cargo.toml b/crates/kilt-dip-primitives/Cargo.toml
index 2afec4987c..2b7f4adce1 100644
--- a/crates/kilt-dip-primitives/Cargo.toml
+++ b/crates/kilt-dip-primitives/Cargo.toml
@@ -39,15 +39,8 @@ 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
cumulus-primitives-core.workspace = true
-parachain-info.workspace = true
[dev-dependencies]
hex-literal.workspace = true
@@ -75,16 +68,10 @@ std = [
"sp-state-machine/std",
"sp-std/std",
"sp-trie/std",
- "rococo-runtime/std",
- "xcm/std",
- "xcm-executor/std",
- "cumulus-pallet-parachain-system/std",
"cumulus-primitives-core/std",
- "parachain-info/std",
]
runtime-benchmarks = [
"kilt-support/runtime-benchmarks",
"pallet-dip-consumer/runtime-benchmarks",
"pallet-dip-provider/runtime-benchmarks",
- "rococo-runtime/runtime-benchmarks"
]
diff --git a/crates/kilt-dip-primitives/src/did.rs b/crates/kilt-dip-primitives/src/did.rs
deleted file mode 100644
index 9f16bd2982..0000000000
--- a/crates/kilt-dip-primitives/src/did.rs
+++ /dev/null
@@ -1,223 +0,0 @@
-// KILT Blockchain – https://botlabs.org
-// Copyright (C) 2019-2024 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
-
-//! Module to deal with cross-chain KILT DIDs.
-
-use did::{
- did_details::{DidPublicKey, DidPublicKeyDetails, DidVerificationKey},
- DidSignature, DidVerificationKeyRelationship,
-};
-use parity_scale_codec::{Decode, Encode};
-use scale_info::TypeInfo;
-use sp_core::RuntimeDebug;
-use sp_runtime::traits::CheckedSub;
-use sp_std::vec::Vec;
-
-use crate::{
- merkle::RevealedDidKey,
- traits::{DidSignatureVerifierContext, DipCallOriginFilter, Incrementable},
-};
-
-/// Type returned by the Merkle proof verifier component of the DIP consumer
-/// after verifying a DIP Merkle proof.
-#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)]
-pub(crate) struct RevealedDidKeysAndSignature {
- /// The keys revelaed in the Merkle proof.
- pub merkle_leaves: RevealedDidKeys,
- /// The [`DIDSignature`] + consumer chain block number to which the DID
- /// signature is anchored.
- pub did_signature: TimeBoundDidSignature,
-}
-
-/// A DID signature anchored to a specific block height.
-#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)]
-pub struct TimeBoundDidSignature {
- /// The signature.
- pub signature: DidSignature,
- /// The block number, in the context of the local executor, to which the
- /// signature is anchored.
- pub block_number: BlockNumber,
-}
-
-#[cfg(feature = "runtime-benchmarks")]
-impl kilt_support::traits::GetWorstCase for TimeBoundDidSignature
-where
- DidSignature: kilt_support::traits::GetWorstCase,
- BlockNumber: Default,
-{
- fn worst_case(context: Context) -> Self {
- Self {
- signature: DidSignature::worst_case(context),
- block_number: BlockNumber::default(),
- }
- }
-}
-
-pub enum RevealedDidKeysSignatureAndCallVerifierError {
- SignatureNotFresh,
- SignatureUnverifiable,
- OriginCheckFailed,
- Internal,
-}
-
-impl From for u8 {
- fn from(value: RevealedDidKeysSignatureAndCallVerifierError) -> Self {
- match value {
- RevealedDidKeysSignatureAndCallVerifierError::SignatureNotFresh => 0,
- RevealedDidKeysSignatureAndCallVerifierError::SignatureUnverifiable => 1,
- RevealedDidKeysSignatureAndCallVerifierError::OriginCheckFailed => 2,
- RevealedDidKeysSignatureAndCallVerifierError::Internal => u8::MAX,
- }
- }
-}
-
-/// Function that tries to verify a DID signature over a given payload by
-/// using one of the DID keys revealed in the Merkle proof. This verifier is
-/// typically used in conjunction with a verifier that takes a user-provided
-/// input Merkle proof, verifies it, and transforms it into a struct that this
-/// and other verifiers can easily consume, e.g., a list of DID keys.
-/// The generic types are the following:
-/// * `Call`: The call to be dispatched on the local chain after verifying the
-/// DID signature.
-/// * `Submitter`: The blockchain account (**not** the identity subject)
-/// submitting the cross-chain transaction (and paying for its execution
-/// fees).
-/// * `DidLocalDetails`: Any information associated to the identity subject that
-/// is stored locally, e.g., under the `IdentityEntries` map of the
-/// `pallet-dip-consumer` pallet.
-/// * `MerkleProofEntries`: The type returned by the Merkle proof verifier that
-/// includes the identity parts revealed in the Merkle proof.
-/// * `ContextProvider`: Provides additional local context (e.g., current block
-/// number) to verify the DID signature.
-/// * `RemoteKeyId`: Definition of a DID key ID as specified by the provider.
-/// * `RemoteAccountId`: Definition of a linked account ID as specified by the
-/// provider.
-/// * `RemoteBlockNumber`: Definition of a block number on the provider chain.
-/// * `CallVerifier`: A type specifying whether the provided `Call` can be
-/// dispatched with the information provided in the DIP proof.
-pub(crate) fn verify_did_signature_for_call<
- Call,
- Submitter,
- DidLocalDetails,
- MerkleProofEntries,
- ContextProvider,
- RemoteKeyId,
- RemoteAccountId,
- RemoteBlockNumber,
- CallVerifier,
->(
- call: &Call,
- submitter: &Submitter,
- local_details: &mut Option,
- merkle_revealed_did_signature: RevealedDidKeysAndSignature,
-) -> Result<
- (DidVerificationKey, DidVerificationKeyRelationship),
- RevealedDidKeysSignatureAndCallVerifierError,
->
-where
- Call: Encode,
- Submitter: Encode,
- ContextProvider: DidSignatureVerifierContext,
- ContextProvider::BlockNumber: Encode + CheckedSub + From + PartialOrd,
- ContextProvider::Hash: Encode,
- ContextProvider::SignedExtra: Encode,
- DidLocalDetails: Incrementable + Default + Encode,
- RemoteAccountId: Clone,
- MerkleProofEntries: sp_std::borrow::Borrow<[RevealedDidKey]>,
- CallVerifier:
- DipCallOriginFilter, DidVerificationKeyRelationship)>,
-{
- cfg_if::cfg_if! {
- if #[cfg(feature = "runtime-benchmarks")] {
- {}
- } else {
- let block_number = ContextProvider::current_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
- };
- frame_support::ensure!(
- is_signature_fresh,
- RevealedDidKeysSignatureAndCallVerifierError::SignatureNotFresh,
- );
- }
- }
- let encoded_payload = (
- call,
- &local_details,
- submitter,
- &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 proof_verification_keys: Vec<(DidVerificationKey, DidVerificationKeyRelationship)> = merkle_revealed_did_signature.merkle_leaves.borrow().iter().filter_map(|RevealedDidKey {
- relationship, details: DidPublicKeyDetails { key, .. }, .. } | {
- let DidPublicKey::PublicVerificationKey(key) = key else { return None };
- if let Ok(vr) = DidVerificationKeyRelationship::try_from(*relationship) {
- // TODO: Fix this logic to avoid cloning
- Some(Ok((key.clone(), vr)))
- } else {
- cfg_if::cfg_if! {
- if #[cfg(feature = "runtime-benchmarks")] {
- None
- } else {
- log::error!("Should never fail to build a VerificationRelationship from the given DidKeyRelationship because we have already made sure the conditions hold.");
- Some(Err(RevealedDidKeysSignatureAndCallVerifierError::Internal))
- }
- }
- }
- }).collect::>()?;
- let valid_signing_key = proof_verification_keys.iter().find(|(verification_key, _)| {
- verification_key
- .verify_signature(&encoded_payload, &merkle_revealed_did_signature.did_signature.signature)
- .is_ok()
- });
- cfg_if::cfg_if! {
- if #[cfg(feature = "runtime-benchmarks")] {
- let default = (
- DidVerificationKey::Ed25519(sp_core::ed25519::Public::from_raw([0u8; 32])),
- DidVerificationKeyRelationship::Authentication,
- );
- let (key, relationship) = valid_signing_key.unwrap_or(&default);
- } else {
- let (key, relationship) = valid_signing_key.ok_or(RevealedDidKeysSignatureAndCallVerifierError::SignatureUnverifiable)?;
- }
- }
-
- if let Some(details) = local_details {
- details.increment();
- } else {
- *local_details = Some(DidLocalDetails::default());
- };
- let res = CallVerifier::check_call_origin_info(call, &(key.clone(), *relationship));
- cfg_if::cfg_if! {
- if #[cfg(feature = "runtime-benchmarks")] {
- drop(res);
- } else {
- res.map_err(|_| RevealedDidKeysSignatureAndCallVerifierError::OriginCheckFailed)?;
- }
- }
- Ok((key.clone(), *relationship))
-}
diff --git a/crates/kilt-dip-primitives/src/lib.rs b/crates/kilt-dip-primitives/src/lib.rs
index fc99b1d5e0..30623e96ca 100644
--- a/crates/kilt-dip-primitives/src/lib.rs
+++ b/crates/kilt-dip-primitives/src/lib.rs
@@ -26,14 +26,18 @@
#![cfg_attr(not(feature = "std"), no_std)]
-pub mod did;
+/// Module to deal with cross-chain Merkle proof as generated by the KILT chain.
pub mod merkle;
+/// Module to deal with cross-chain state proofs.
pub mod state_proofs;
+/// Collection of traits used throughout the crate and useful for both providers
+/// and consumers.
pub mod traits;
pub mod utils;
+/// Verifier module containing types that implement the verifier component to be
+/// deployed both on a sibling parachain and on a parent relaychain.
pub mod verifier;
-pub use state_proofs::relaychain::RelayStateRootsViaRelayStorePallet;
-pub use traits::{FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet};
-pub use utils::BoundedBlindedValue;
+pub use merkle::latest::*;
+pub use traits::RelayStateRootsViaRelayStorePallet;
pub use verifier::*;
diff --git a/crates/kilt-dip-primitives/src/merkle.rs b/crates/kilt-dip-primitives/src/merkle.rs
deleted file mode 100644
index 65d2d90d8a..0000000000
--- a/crates/kilt-dip-primitives/src/merkle.rs
+++ /dev/null
@@ -1,416 +0,0 @@
-// KILT Blockchain – https://botlabs.org
-// Copyright (C) 2019-2024 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
-
-//! Module to deal with cross-chain Merkle proof as generated by the KILT chain.
-
-use did::{did_details::DidPublicKeyDetails, DidVerificationKeyRelationship};
-use frame_support::{traits::ConstU32, DefaultNoBound, RuntimeDebug};
-use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
-use scale_info::TypeInfo;
-use sp_runtime::{BoundedVec, SaturatedConversion};
-use sp_std::{fmt::Debug, vec::Vec};
-use sp_trie::{verify_trie_proof, LayoutV1};
-
-/// Type of a Merkle proof containing DID-related information.
-#[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,
-}
-
-#[cfg(feature = "runtime-benchmarks")]
-impl kilt_support::traits::GetWorstCase for DidMerkleProof
-where
- BlindedValues: kilt_support::traits::GetWorstCase,
- Leaf: Default + Clone,
-{
- fn worst_case(context: Context) -> Self {
- Self {
- blinded: BlindedValues::worst_case(context),
- revealed: sp_std::vec![Leaf::default(); 64],
- }
- }
-}
-
-/// Relationship of a key to a DID Document.
-#[derive(Clone, Copy, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord, MaxEncodedLen)]
-pub enum DidKeyRelationship {
- Encryption,
- Verification(DidVerificationKeyRelationship),
-}
-
-impl From for DidKeyRelationship {
- fn from(value: DidVerificationKeyRelationship) -> Self {
- Self::Verification(value)
- }
-}
-
-impl TryFrom for DidVerificationKeyRelationship {
- type Error = ();
-
- fn try_from(value: DidKeyRelationship) -> Result {
- if let DidKeyRelationship::Verification(rel) = value {
- Ok(rel)
- } else {
- Err(())
- }
- }
-}
-
-/// The key of a Merkle leaf revealing a DID key for a DID Document.
-#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)]
-pub struct DidKeyMerkleKey(pub KeyId, pub DidKeyRelationship);
-
-impl From<(KeyId, DidKeyRelationship)> for DidKeyMerkleKey {
- fn from(value: (KeyId, DidKeyRelationship)) -> Self {
- Self(value.0, value.1)
- }
-}
-/// The value of a Merkle leaf revealing a DID key for a DID Document.
-#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)]
-pub struct DidKeyMerkleValue(pub DidPublicKeyDetails);
-
-impl From>
- for DidKeyMerkleValue
-{
- fn from(value: DidPublicKeyDetails) -> Self {
- Self(value)
- }
-}
-
-/// The key of a Merkle leaf revealing the web3name linked to a DID Document.
-#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)]
-pub struct Web3NameMerkleKey(pub Web3Name);
-
-impl From for Web3NameMerkleKey {
- fn from(value: Web3Name) -> Self {
- Self(value)
- }
-}
-/// The value of a Merkle leaf revealing the web3name linked to a DID Document.
-#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)]
-pub struct Web3NameMerkleValue(pub BlockNumber);
-
-impl From for Web3NameMerkleValue {
- fn from(value: BlockNumber) -> Self {
- Self(value)
- }
-}
-
-/// The key of a Merkle leaf revealing an account linked to a DID Document.
-#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)]
-pub struct LinkedAccountMerkleKey(pub AccountId);
-
-impl From for LinkedAccountMerkleKey {
- fn from(value: AccountId) -> Self {
- Self(value)
- }
-}
-/// The value of a Merkle leaf revealing an account linked to a DID
-/// Document.
-#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)]
-pub struct LinkedAccountMerkleValue;
-
-impl From<()> for LinkedAccountMerkleValue {
- fn from(_value: ()) -> Self {
- Self
- }
-}
-
-/// All possible Merkle leaf types that can be revealed as part of a DIP
-/// identity Merkle proof.
-#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)]
-pub enum RevealedDidMerkleProofLeaf {
- DidKey(DidKeyMerkleKey, DidKeyMerkleValue),
- Web3Name(Web3NameMerkleKey, Web3NameMerkleValue),
- LinkedAccount(LinkedAccountMerkleKey, LinkedAccountMerkleValue),
-}
-
-#[cfg(feature = "runtime-benchmarks")]
-impl Default
- for RevealedDidMerkleProofLeaf
-where
- KeyId: Default,
- BlockNumber: Default,
-{
- fn default() -> Self {
- Self::DidKey(
- (KeyId::default(), DidVerificationKeyRelationship::Authentication.into()).into(),
- DidPublicKeyDetails {
- key: did::did_details::DidVerificationKey::Ed25519(sp_core::ed25519::Public::from_raw([0u8; 32]))
- .into(),
- block_number: BlockNumber::default(),
- }
- .into(),
- )
- }
-}
-
-impl
- RevealedDidMerkleProofLeaf
-where
- KeyId: Encode,
- Web3Name: Encode,
- LinkedAccountId: Encode,
-{
- pub fn encoded_key(&self) -> Vec {
- match self {
- RevealedDidMerkleProofLeaf::DidKey(key, _) => key.encode(),
- RevealedDidMerkleProofLeaf::Web3Name(key, _) => key.encode(),
- RevealedDidMerkleProofLeaf::LinkedAccount(key, _) => key.encode(),
- }
- }
-}
-
-impl
- RevealedDidMerkleProofLeaf
-where
- AccountId: Encode,
- BlockNumber: Encode,
-{
- pub fn encoded_value(&self) -> Vec {
- match self {
- RevealedDidMerkleProofLeaf::DidKey(_, value) => value.encode(),
- RevealedDidMerkleProofLeaf::Web3Name(_, value) => value.encode(),
- RevealedDidMerkleProofLeaf::LinkedAccount(_, value) => value.encode(),
- }
- }
-}
-
-/// The details of a DID key after it has been successfully verified in a Merkle
-/// proof.
-#[derive(Clone, Encode, Decode, PartialEq, MaxEncodedLen, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)]
-pub struct RevealedDidKey {
- /// The key ID, according to the provider's definition.
- pub id: KeyId,
- /// The key relationship to the subject's DID Document.
- pub relationship: DidKeyRelationship,
- /// The details of the DID Key, including its creation block number on the
- /// provider chain.
- pub details: DidPublicKeyDetails,
-}
-
-/// The details of a web3name after it has been successfully verified in a
-/// Merkle proof.
-#[derive(Clone, Encode, Decode, PartialEq, MaxEncodedLen, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)]
-pub struct RevealedWeb3Name {
- /// The web3name.
- pub web3_name: Web3Name,
- /// The block number on the provider chain in which it was linked to the DID
- /// subject.
- pub claimed_at: BlockNumber,
-}
-
-/// The complete set of information that is provided by the DIP Merkle proof
-/// verifier upon successful verification of a DIP Merkle proof.
-#[derive(Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen, Encode, Decode, DefaultNoBound)]
-pub struct RevealedDidMerkleProofLeaves<
- KeyId,
- AccountId,
- BlockNumber,
- Web3Name,
- LinkedAccountId,
- const MAX_REVEALED_KEYS_COUNT: u32,
- const MAX_REVEALED_ACCOUNTS_COUNT: u32,
-> {
- /// The list of [`RevealedDidKey`]s revealed in the Merkle proof, up to a
- /// maximum of `MAX_REVEALED_KEYS_COUNT`.
- pub did_keys: BoundedVec, ConstU32>,
- /// The optional [`RevealedWeb3Name`] revealed in the Merkle proof.
- pub web3_name: Option>,
- /// The list of linked accounts revealed in the Merkle proof, up to a
- /// maximum of `MAX_REVEALED_ACCOUNTS_COUNT`.
- pub linked_accounts: BoundedVec>,
-}
-
-impl<
- KeyId,
- AccountId,
- BlockNumber,
- Web3Name,
- LinkedAccountId,
- const MAX_REVEALED_KEYS_COUNT: u32,
- const MAX_REVEALED_ACCOUNTS_COUNT: u32,
- > sp_std::borrow::Borrow<[RevealedDidKey]>
- for RevealedDidMerkleProofLeaves<
- KeyId,
- AccountId,
- BlockNumber,
- Web3Name,
- LinkedAccountId,
- MAX_REVEALED_KEYS_COUNT,
- MAX_REVEALED_ACCOUNTS_COUNT,
- >
-{
- fn borrow(&self) -> &[RevealedDidKey] {
- self.did_keys.borrow()
- }
-}
-
-pub enum DidMerkleProofVerifierError {
- InvalidMerkleProof,
- TooManyRevealedKeys,
- TooManyRevealedAccounts,
-}
-
-impl From for u8 {
- fn from(value: DidMerkleProofVerifierError) -> Self {
- match value {
- DidMerkleProofVerifierError::InvalidMerkleProof => 0,
- DidMerkleProofVerifierError::TooManyRevealedKeys => 1,
- DidMerkleProofVerifierError::TooManyRevealedAccounts => 2,
- }
- }
-}
-
-/// A function that verifies a DIP Merkle proof revealing some leaves
-/// representing parts of a KILT DID identity stored on the KILT chain.
-/// If cross-chain DID signatures are not required for the specific use case,
-/// this verifier can also be used on its own, without any DID signature
-/// verification.
-/// The Merkle proof is assumed to have been generated using one of the
-/// versioned identity commitment generators, as shown in the [KILT runtime
-/// definitions](../../../runtimes/common/src/dip/README.md).
-/// The generic types are the following:
-/// * `Hasher`: The hasher used by the producer to hash the Merkle leaves and
-/// produce the identity commitment.
-/// * `KeyId`: The type of a DID key ID according to the producer's definition.
-/// * `AccountId`: The type of an account ID according to the producer's
-/// definition.
-/// * `BlockNumber`: The type of a block number according to the producer's
-/// definition.
-/// * `Web3Name`: The type of a web3names according to the producer's
-/// definition.
-/// * `LinkedAccountId`: The type of a DID-linked account ID according to the
-/// producer's definition.
-/// * `MAX_REVEALED_KEYS_COUNT`: The maximum number of DID keys that are
-/// supported when verifying the Merkle proof.
-/// * `MAX_REVEALED_ACCOUNTS_COUNT`: The maximum number of linked accounts that
-/// are supported when verifying the Merkle proof.
-pub(crate) fn verify_dip_merkle_proof<
- Hasher,
- KeyId,
- AccountId,
- BlockNumber,
- Web3Name,
- LinkedAccountId,
- const MAX_REVEALED_KEYS_COUNT: u32,
- const MAX_REVEALED_ACCOUNTS_COUNT: u32,
->(
- identity_commitment: &Hasher::Out,
- proof: DidMerkleProof<
- crate::BoundedBlindedValue,
- RevealedDidMerkleProofLeaf,
- >,
-) -> Result<
- RevealedDidMerkleProofLeaves<
- KeyId,
- AccountId,
- BlockNumber,
- Web3Name,
- LinkedAccountId,
- MAX_REVEALED_KEYS_COUNT,
- MAX_REVEALED_ACCOUNTS_COUNT,
- >,
- DidMerkleProofVerifierError,
->
-where
- BlockNumber: Encode + Clone,
- Hasher: sp_core::Hasher,
- KeyId: Encode + Clone,
- AccountId: Encode + Clone,
- LinkedAccountId: Encode + Clone,
- Web3Name: Encode + Clone,
-{
- // 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>)>.
- let proof_leaves = proof
- .revealed
- .iter()
- .map(|leaf| (leaf.encoded_key(), Some(leaf.encoded_value())))
- .collect::, Option>)>>();
- let res = verify_trie_proof::, _, _, _>(identity_commitment, &proof.blinded, &proof_leaves);
- cfg_if::cfg_if! {
- if #[cfg(feature = "runtime-benchmarks")] {
- drop(res);
- } else {
- res.map_err(|_| DidMerkleProofVerifierError::InvalidMerkleProof)?;
- }
- }
-
- // 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.
- #[allow(clippy::type_complexity)]
- let (did_keys, web3_name, linked_accounts): (
- BoundedVec, ConstU32>,
- Option>,
- BoundedVec>,
- ) = proof.revealed.into_iter().try_fold(
- (
- BoundedVec::with_bounded_capacity(MAX_REVEALED_KEYS_COUNT.saturated_into()),
- None,
- BoundedVec::with_bounded_capacity(MAX_REVEALED_ACCOUNTS_COUNT.saturated_into()),
- ),
- |(mut keys, web3_name, mut linked_accounts), leaf| match leaf {
- RevealedDidMerkleProofLeaf::DidKey(key_id, key_value) => {
- let res = keys.try_push(RevealedDidKey {
- id: key_id.0,
- relationship: key_id.1,
- details: key_value.0,
- });
- cfg_if::cfg_if! {
- if #[cfg(feature = "runtime-benchmarks")] {
- drop(res);
- } else {
- res.map_err(|_| DidMerkleProofVerifierError::TooManyRevealedKeys)?;
- }
- }
-
- Ok::<_, DidMerkleProofVerifierError>((keys, web3_name, linked_accounts))
- }
- RevealedDidMerkleProofLeaf::Web3Name(revealed_web3_name, details) => Ok((
- keys,
- Some(RevealedWeb3Name {
- web3_name: revealed_web3_name.0,
- claimed_at: details.0,
- }),
- linked_accounts,
- )),
- RevealedDidMerkleProofLeaf::LinkedAccount(account_id, _) => {
- let res = linked_accounts.try_push(account_id.0);
- cfg_if::cfg_if! {
- if #[cfg(feature = "runtime-benchmarks")] {
- drop(res);
- } else {
- res.map_err(|_| DidMerkleProofVerifierError::TooManyRevealedAccounts)?;
- }
- }
-
- Ok::<_, DidMerkleProofVerifierError>((keys, web3_name, linked_accounts))
- }
- },
- )?;
-
- Ok(RevealedDidMerkleProofLeaves {
- did_keys,
- web3_name,
- linked_accounts,
- })
-}
diff --git a/crates/kilt-dip-primitives/src/merkle/mod.rs b/crates/kilt-dip-primitives/src/merkle/mod.rs
new file mode 100644
index 0000000000..552a922f8b
--- /dev/null
+++ b/crates/kilt-dip-primitives/src/merkle/mod.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 mod v0;
+
+pub mod latest {
+ pub use super::v0::*;
+}
diff --git a/crates/kilt-dip-primitives/src/merkle/v0.rs b/crates/kilt-dip-primitives/src/merkle/v0.rs
new file mode 100644
index 0000000000..d6124bb767
--- /dev/null
+++ b/crates/kilt-dip-primitives/src/merkle/v0.rs
@@ -0,0 +1,1303 @@
+// 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
+
+//! Module to deal with cross-chain Merkle proof as generated by the KILT chain.
+use did::{
+ did_details::{DidPublicKey, DidPublicKeyDetails},
+ DidSignature, DidVerificationKeyRelationship,
+};
+use frame_support::ensure;
+use pallet_dip_provider::IdentityCommitmentOf;
+use parity_scale_codec::{Codec, Decode, Encode, MaxEncodedLen};
+use scale_info::TypeInfo;
+use sp_core::{ConstU32, U256};
+use sp_runtime::{
+ generic::Header,
+ traits::{AtLeast32BitUnsigned, Hash, Header as HeaderT, MaybeDisplay, Member},
+ BoundedVec, SaturatedConversion,
+};
+use sp_std::{fmt::Debug, vec::Vec};
+use sp_trie::{verify_trie_proof, LayoutV1};
+
+use crate::{
+ state_proofs::{verify_storage_value_proof, verify_storage_value_proof_with_decoder, MerkleProofError},
+ traits::{BenchmarkDefault, GetWithArg},
+ utils::{
+ calculate_dip_identity_commitment_storage_key_for_runtime, calculate_parachain_head_storage_key,
+ BoundedBlindedValue, OutputOf,
+ },
+};
+
+/// The state proof for a parachain head.
+///
+/// The generic types indicate the following:
+/// * `RelayBlockNumber`: The `BlockNumber` definition of the relaychain.
+#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo)]
+pub struct ProviderHeadStateProof {
+ pub(crate) relay_block_number: RelayBlockNumber,
+ pub(crate) proof: BoundedBlindedValue,
+}
+
+#[cfg(feature = "runtime-benchmarks")]
+impl kilt_support::traits::GetWorstCase for ProviderHeadStateProof
+where
+ RelayBlockNumber: Default,
+{
+ fn worst_case(context: Context) -> Self {
+ Self {
+ relay_block_number: RelayBlockNumber::default(),
+ proof: BoundedBlindedValue::worst_case(context),
+ }
+ }
+}
+
+/// The state proof for a DIP commitment.
+#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo)]
+pub struct DipCommitmentStateProof(pub(crate) BoundedBlindedValue);
+
+#[cfg(feature = "runtime-benchmarks")]
+impl kilt_support::traits::GetWorstCase for DipCommitmentStateProof {
+ fn worst_case(context: Context) -> Self {
+ Self(BoundedBlindedValue::worst_case(context))
+ }
+}
+
+/// The Merkle proof for a KILT DID.
+///
+/// The generic types indicate the following:
+/// * `ProviderDidKeyId`: The DID key ID type configured by the provider.
+/// * `ProviderAccountId`: The `AccountId` type configured by the provider.
+/// * `ProviderBlockNumber`: The `BlockNumber` type configured by the provider.
+/// * `ProviderWeb3Name`: The web3name type configured by the provider.
+/// * `ProviderLinkableAccountId`: The linkable account ID type configured by
+/// the provider.
+#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo)]
+pub struct DidMerkleProof<
+ ProviderDidKeyId,
+ ProviderAccountId,
+ ProviderBlockNumber,
+ ProviderWeb3Name,
+ ProviderLinkableAccountId,
+> {
+ pub(crate) blinded: BoundedBlindedValue,
+ pub(crate) revealed: Vec<
+ RevealedDidMerkleProofLeaf<
+ ProviderDidKeyId,
+ ProviderAccountId,
+ ProviderBlockNumber,
+ ProviderWeb3Name,
+ ProviderLinkableAccountId,
+ >,
+ >,
+}
+
+impl
+ DidMerkleProof
+{
+ pub fn new(
+ blinded: BoundedBlindedValue,
+ revealed: Vec<
+ RevealedDidMerkleProofLeaf<
+ ProviderDidKeyId,
+ ProviderAccountId,
+ ProviderBlockNumber,
+ ProviderWeb3Name,
+ ProviderLinkableAccountId,
+ >,
+ >,
+ ) -> Self {
+ Self { blinded, revealed }
+ }
+}
+
+#[cfg(feature = "runtime-benchmarks")]
+impl<
+ ProviderDidKeyId,
+ ProviderAccountId,
+ ProviderBlockNumber,
+ ProviderWeb3Name,
+ ProviderLinkableAccountId,
+ Context,
+ > kilt_support::traits::GetWorstCase
+ for DidMerkleProof<
+ ProviderDidKeyId,
+ ProviderAccountId,
+ ProviderBlockNumber,
+ ProviderWeb3Name,
+ ProviderLinkableAccountId,
+ > where
+ ProviderDidKeyId: Default + Clone,
+ ProviderAccountId: Clone,
+ ProviderBlockNumber: Default + Clone,
+ ProviderWeb3Name: Clone,
+ ProviderLinkableAccountId: Clone,
+{
+ fn worst_case(context: Context) -> Self {
+ Self {
+ blinded: BoundedBlindedValue::worst_case(context),
+ revealed: sp_std::vec![RevealedDidMerkleProofLeaf::default(); 64],
+ }
+ }
+}
+
+/// A DID signature anchored to a specific block height.
+///
+/// The generic types indicate the following:
+/// * `BlockNumber`: The `BlockNumber` definition of the chain consuming (i.e.,
+/// validating) this signature.
+#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo)]
+pub struct TimeBoundDidSignature {
+ /// The signature.
+ pub(crate) signature: DidSignature,
+ /// The block number until the signature is to be considered valid.
+ pub(crate) valid_until: BlockNumber,
+}
+
+#[cfg(feature = "runtime-benchmarks")]
+impl kilt_support::traits::GetWorstCase for TimeBoundDidSignature
+where
+ DidSignature: kilt_support::traits::GetWorstCase,
+ BlockNumber: Default,
+{
+ fn worst_case(context: Context) -> Self {
+ Self {
+ signature: DidSignature::worst_case(context),
+ valid_until: BlockNumber::default(),
+ }
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, TypeInfo)]
+pub enum Error {
+ InvalidRelayHeader,
+ RelayBlockNotFound,
+ RelayStateRootNotFound,
+ InvalidDidMerkleProof,
+ TooManyLeavesRevealed,
+ InvalidSignatureTime,
+ InvalidDidKeyRevealed,
+ ParaHeadMerkleProof(MerkleProofError),
+ DipCommitmentMerkleProof(MerkleProofError),
+ Internal,
+}
+
+impl From for u8 {
+ fn from(value: Error) -> Self {
+ match value {
+ // DO NOT USE 0
+ // Errors of different sub-parts are separated by a `u8::MAX`.
+ // A value of 0 would make it confusing whether it's the previous sub-part error (u8::MAX)
+ // or the new sub-part error (u8::MAX + 0).
+ Error::InvalidRelayHeader => 1,
+ Error::RelayBlockNotFound => 2,
+ Error::RelayStateRootNotFound => 3,
+ Error::InvalidDidMerkleProof => 4,
+ Error::TooManyLeavesRevealed => 5,
+ Error::InvalidSignatureTime => 6,
+ Error::InvalidDidKeyRevealed => 7,
+ Error::ParaHeadMerkleProof(error) => match error {
+ MerkleProofError::InvalidProof => 11,
+ MerkleProofError::RequiredLeafNotRevealed => 12,
+ MerkleProofError::ResultDecoding => 13,
+ },
+ Error::DipCommitmentMerkleProof(error) => match error {
+ MerkleProofError::InvalidProof => 21,
+ MerkleProofError::RequiredLeafNotRevealed => 22,
+ MerkleProofError::ResultDecoding => 23,
+ },
+ Error::Internal => u8::MAX,
+ }
+ }
+}
+
+/// A DIP proof submitted to a relaychain consumer.
+///
+/// The generic types indicate the following:
+/// * `RelayBlockNumber`: The `BlockNumber` definition of the relaychain.
+/// * `RelayHasher`: The hashing algorithm used by the relaychain.
+/// * `KiltDidKeyId`: The DID key ID type configured by the KILT chain.
+/// * `KiltAccountId`: The `AccountId` type configured by the KILT chain.
+/// * `KiltBlockNumber`: The `BlockNumber` type configured by the KILT chain.
+/// * `KiltWeb3Name`: The web3name type configured by the KILT chain.
+/// * `KiltLinkableAccountId`: The linkable account ID type configured by the
+/// KILT chain.
+#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo)]
+pub struct RelayDipDidProof<
+ RelayBlockNumber: Copy + Into + TryFrom,
+ RelayHasher: Hash,
+ KiltDidKeyId,
+ KiltAccountId,
+ KiltBlockNumber,
+ KiltWeb3Name,
+ KiltLinkableAccountId,
+> {
+ /// The relaychain header for the relaychain block specified in the
+ /// `provider_head_proof`.
+ pub(crate) relay_header: Header,
+ /// The state proof for the given parachain head.
+ pub(crate) provider_head_proof: ProviderHeadStateProof,
+ /// The raw state proof for the DIP commitment of the given subject.
+ pub(crate) dip_commitment_proof: DipCommitmentStateProof,
+ /// The Merkle proof of the subject's DID details.
+ pub(crate) dip_proof:
+ DidMerkleProof,
+ /// The cross-chain DID signature.
+ pub(crate) signature: TimeBoundDidSignature,
+}
+
+impl<
+ RelayBlockNumber: Member + sp_std::hash::Hash + Copy + MaybeDisplay + AtLeast32BitUnsigned + Codec + Into + TryFrom,
+ RelayHasher: Hash,
+ KiltDidKeyId,
+ KiltAccountId,
+ KiltBlockNumber,
+ KiltWeb3Name,
+ KiltLinkableAccountId,
+ >
+ RelayDipDidProof<
+ RelayBlockNumber,
+ RelayHasher,
+ KiltDidKeyId,
+ KiltAccountId,
+ KiltBlockNumber,
+ KiltWeb3Name,
+ KiltLinkableAccountId,
+ >
+{
+ /// Verifies the relaychain part of the state proof using the provided block
+ /// hash.
+ #[allow(clippy::type_complexity)]
+ pub fn verify_relay_header_with_block_hash(
+ self,
+ block_hash: &OutputOf,
+ ) -> Result<
+ RelayDipDidProofWithVerifiedRelayStateRoot<
+ OutputOf,
+ RelayBlockNumber,
+ KiltDidKeyId,
+ KiltAccountId,
+ KiltBlockNumber,
+ KiltWeb3Name,
+ KiltLinkableAccountId,
+ >,
+ Error,
+ > {
+ if block_hash != &self.relay_header.hash() {
+ return Err(Error::InvalidRelayHeader);
+ }
+
+ Ok(RelayDipDidProofWithVerifiedRelayStateRoot {
+ relay_state_root: self.relay_header.state_root,
+ provider_head_proof: self.provider_head_proof,
+ dip_commitment_proof: self.dip_commitment_proof,
+ dip_proof: self.dip_proof,
+ signature: self.signature,
+ })
+ }
+
+ /// Verifies the relaychain part of the state proof using the block hash
+ /// returned by the provided implementation.
+ ///
+ /// The generic types indicate the following:
+ /// * `RelayHashStore`: The type that returns a relaychain block hash given
+ /// a relaychain block number.
+ #[allow(clippy::type_complexity)]
+ pub fn verify_relay_header(
+ self,
+ ) -> Result<
+ RelayDipDidProofWithVerifiedRelayStateRoot<
+ OutputOf,
+ RelayBlockNumber,
+ KiltDidKeyId,
+ KiltAccountId,
+ KiltBlockNumber,
+ KiltWeb3Name,
+ KiltLinkableAccountId,
+ >,
+ Error,
+ >
+ where
+ RelayHashStore: GetWithArg>>,
+ {
+ let relay_block_hash = RelayHashStore::get(&self.relay_header.number).ok_or(Error::RelayBlockNotFound)?;
+ self.verify_relay_header_with_block_hash(&relay_block_hash)
+ }
+}
+
+/// A DIP proof submitted to a relaychain consumer that has had the proof header
+/// verified against a given block hash.
+///
+/// The generic types indicate the following:
+/// * `StateRoot`: The type of the state root used by the relaychain.
+/// * `RelayBlockNumber`: The `BlockNumber` definition of the relaychain.
+/// * `KiltDidKeyId`: The DID key ID type configured by the KILT chain.
+/// * `KiltAccountId`: The `AccountId` type configured by the KILT chain.
+/// * `KiltBlockNumber`: The `BlockNumber` type configured by the KILT chain.
+/// * `KiltWeb3Name`: The web3name type configured by the KILT chain.
+/// * `KiltLinkableAccountId`: The linkable account ID type configured by the
+/// KILT chain.
+#[derive(Debug)]
+pub struct RelayDipDidProofWithVerifiedRelayStateRoot<
+ StateRoot,
+ RelayBlockNumber,
+ KiltDidKeyId,
+ KiltAccountId,
+ KiltBlockNumber,
+ KiltWeb3Name,
+ KiltLinkableAccountId,
+> {
+ /// The verified state root for the relaychain at the block specified in the
+ /// proof.
+ pub(crate) relay_state_root: StateRoot,
+ /// The state proof for the given parachain head.
+ pub(crate) provider_head_proof: ProviderHeadStateProof,
+ /// The raw state proof for the DIP commitment of the given subject.
+ pub(crate) dip_commitment_proof: DipCommitmentStateProof,
+ /// The Merkle proof of the subject's DID details.
+ pub(crate) dip_proof:
+ DidMerkleProof,
+ /// The cross-chain DID signature.
+ pub(crate) signature: TimeBoundDidSignature,
+}
+
+impl<
+ StateRoot,
+ RelayBlockNumber,
+ KiltDidKeyId,
+ KiltAccountId,
+ KiltBlockNumber,
+ KiltWeb3Name,
+ KiltLinkableAccountId,
+ >
+ RelayDipDidProofWithVerifiedRelayStateRoot<
+ StateRoot,
+ RelayBlockNumber,
+ KiltDidKeyId,
+ KiltAccountId,
+ KiltBlockNumber,
+ KiltWeb3Name,
+ KiltLinkableAccountId,
+ > where
+ KiltBlockNumber: BenchmarkDefault,
+{
+ /// Verifies the head data of the state proof for the provider with the
+ /// given para ID.
+ ///
+ /// The generic types indicate the following:
+ /// * `RelayHasher`: The head data hashing algorithm used by the relaychain.
+ /// * `ProviderHeader`: The type of the parachain header to be revealed in
+ /// the state proof.
+ #[allow(clippy::type_complexity)]
+ pub fn verify_provider_head_proof(
+ self,
+ provider_para_id: u32,
+ ) -> Result<
+ DipDidProofWithVerifiedRelayStateRoot<
+ OutputOf,
+ KiltDidKeyId,
+ KiltAccountId,
+ KiltBlockNumber,
+ KiltWeb3Name,
+ KiltLinkableAccountId,
+ RelayBlockNumber,
+ >,
+ Error,
+ >
+ where
+ RelayHasher: Hash