From ee92d76fe0c08a7855ec06331c52895834a1e510 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Thu, 16 Nov 2023 10:43:26 +0100 Subject: [PATCH 01/17] Documentation for provider pallet --- pallets/pallet-dip-consumer/Cargo.toml | 2 +- pallets/pallet-dip-consumer/README.md | 0 pallets/pallet-dip-provider/Cargo.toml | 2 +- pallets/pallet-dip-provider/README.md | 50 ++++++++++++++++++ pallets/pallet-dip-provider/src/lib.rs | 71 +++++++++++++++++++++++++- pallets/pallet-relay-store/Cargo.toml | 2 +- pallets/pallet-relay-store/README.md | 0 7 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 pallets/pallet-dip-consumer/README.md create mode 100644 pallets/pallet-dip-provider/README.md create mode 100644 pallets/pallet-relay-store/README.md diff --git a/pallets/pallet-dip-consumer/Cargo.toml b/pallets/pallet-dip-consumer/Cargo.toml index 5025ad90dd..ea5ece3738 100644 --- a/pallets/pallet-dip-consumer/Cargo.toml +++ b/pallets/pallet-dip-consumer/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true homepage.workspace = true license-file.workspace = true name = "pallet-dip-consumer" -readme.workspace = true +readme = "README.md" repository.workspace = true version.workspace = true diff --git a/pallets/pallet-dip-consumer/README.md b/pallets/pallet-dip-consumer/README.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pallets/pallet-dip-provider/Cargo.toml b/pallets/pallet-dip-provider/Cargo.toml index 5e22e357a1..bde4738651 100644 --- a/pallets/pallet-dip-provider/Cargo.toml +++ b/pallets/pallet-dip-provider/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true homepage.workspace = true license-file.workspace = true name = "pallet-dip-provider" -readme.workspace = true +readme = "README.md" repository.workspace = true version.workspace = true diff --git a/pallets/pallet-dip-provider/README.md b/pallets/pallet-dip-provider/README.md new file mode 100644 index 0000000000..f486bcd63e --- /dev/null +++ b/pallets/pallet-dip-provider/README.md @@ -0,0 +1,50 @@ +# Decentralized Identity Provider (DIP) pallet + +This pallet is a core component of the Decentralized Identity Provider protocol. +It enables a Substrate-based chain (provider) to bridge the identities of its users to other connected chains (consumers) trustlessly. +A consumer chain is *connected* to a provider if there is a way for the consumer chain to verify state proofs about parts of the state of the provider chain. + +The pallet is agnostic over the chain-specific definition of *identity*, and delegates the definition of it to the provider chain's runtime. + +What the pallet stores are *identity commitments*, which are opaque byte blobs put in the pallet storage and on which the cross-chain identity bridging protocol can be built. +As for identities, the definition of an identity commitment must be provided by the runtime and is therefore provider-specific. +Naturally, this definition must be made available to consumers willing to integrate the identities living on the provider chain. + +Because providers and consumers evolve at different speeds, identity commitments are versioned. +This allows the provider chain to upgrade to a newer commitment scheme, while still giving its users the possibility to use the old version, if the chains on which they want to use their identity does not yet support the new scheme. + +Identity commitments can be replaced (e.g., if something in the identity info changes), or removed altogether by the identity subject. +After removal, the identity becomes unusable cross-chain, although it will still continue to exist on the provider chain and will be usable for local operations. + +## The `Config` trait + +Being chain-agnostic, most of the runtime configurations must be passed to the pallet's `Config` trait. Specifically: + +* `type CommitOriginCheck: EnsureOrigin`: The check ensuring a runtime origin is allowed to generate and remove identity commitments. +* `type CommitOrigin: SubmitterInfo`: The resulting origin if `CommitOriginCheck` returns with errors. The origin is not required to be an `AccountId`, but must include information about the `AccountId` of the tx submitter. +* `type Identifier: Parameter + MaxEncodedLen`: The type of an identifier used to retrieve identity information about a subject. +* `type IdentityCommitmentGenerator: IdentityCommitmentGenerator`: The type responsible for generating identity commitments, given the identity information associated to a given `Identifier`. +* `type IdentityProvider: IdentityProvider`: The type responsible for retrieving the information associated to a subject given their identifier. The information can potentially be retrieved from any source, using a combination of on-chain and off-chain solutions. +* `type IdentityProvider: IdentityProvider`: Customizable external logic to handle events in which a new identity commitment is generated or removed. +* `type RuntimeEvent: From> + IsType<::RuntimeEvent>`: The aggregate `Event` type. + +## Storage + +The pallet contains a single storage element, the `IdentityCommitments` double map. +Its first key is the `Identifier` of subjects, while the second key is the commitment version. +The values are identity commitments. + +As mentioned above, a double map allows the same subject to have one commitment for each version supported by the provider, without forcing consumers to upgrade to a new version to support the latest commitment scheme. + +## Events + +The pallet generates two events: a `VersionedIdentityCommitted` and a `VersionedIdentityDeleted`. + +The `VersionedIdentityCommited` is called whenever a new commitment is stored, and contains information about the `Identifier` of the subject, the value of the commitment, and the commitment version. + +Similarly, the `VersionedIdentityDeleted`, is called whenever a commitment is deleted, and contains information about the `Identifier` of the subject and the version of the commitment deleted. + +## Calls + +* `pub fn commit_identity(origin: OriginFor, identifier: T::Identifier, version: Option ) -> DispatchResult`: Generate a new versioned commitment for the subject identified by the provided `Identifier`. If an old commitment for the same version is present, it is overridden. Hooks are called before the new commitment is stored, and optionally before the old one is replaced. +* `pub fn delete_identity_commitment(origin: OriginFor, identifier: T::Identifier, version: Option) -> DispatchResult`: Delete an identity commitment of a specific version for a specific `Identifier`. If a commitment of the provided version does not exist for the given `Identifier`, an error is returned. Hooks are called after the commitment has been removed. diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index b9819b51bd..ca0819ddba 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -16,10 +16,40 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -// TODO: Pallet description +//! This pallet is a core component of the Decentralized Identity Provider +//! protocol. It enables a Substrate-based chain (provider) to bridge the +//! identities of its users to other connected chains (consumers) trustlessly. A +//! consumer chain is *connected* to a provider if there is a way for the +//! consumer chain to verify state proofs about parts of the state of the +//! provider chain. + +//! The pallet is agnostic over the chain-specific definition of *identity*, and +//! delegates the definition of it to the provider chain's runtime. + +//! What the pallet stores are *identity commitments*, which are opaque byte +//! blobs put in the pallet storage and on which the cross-chain identity +//! bridging protocol can be built. As for identities, the definition of an +//! identity commitment must be provided by the runtime and is therefore +//! provider-specific. Naturally, this definition must be made available to +//! consumers willing to integrate the identities living on the provider chain. + +//! Because providers and consumers evolve at different speeds, identity +//! commitments are versioned. This allows the provider chain to upgrade to a +//! newer commitment scheme, while still giving its users the possibility to use +//! the old version, if the chains on which they want to use their identity does +//! not yet support the new scheme. + +//! Identity commitments can be replaced (e.g., if something in the identity +//! info changes), or removed altogether by the identity subject. After removal, +//! the identity becomes unusable cross-chain, although it will still continue +//! to exist on the provider chain and will be usable for local operations. #![cfg_attr(not(feature = "std"), no_std)] +// !!! When Rust docs changes here, make sure to update the crate README.md file +// as well. +// !!! + pub mod traits; pub use crate::{ @@ -47,15 +77,35 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { + /// The check ensuring a given runtime origin is allowed to generate and + /// remove identity commitments. type CommitOriginCheck: EnsureOriginWithArg; + /// The resulting origin if `CommitOriginCheck` returns with errors. The + /// origin is not required to be an `AccountId`, but must include + /// information about the `AccountId` of the tx submitter. type CommitOrigin: SubmitterInfo; + /// The type of an identifier used to retrieve identity information + /// about a subject. type Identifier: Parameter + MaxEncodedLen; + /// The type responsible for generating identity commitments, given the + /// identity information associated to a given `Identifier`. type IdentityCommitmentGenerator: IdentityCommitmentGenerator; + /// The type responsible for retrieving the information associated to a + /// subject given their identifier. The information can potentially be + /// retrieved from any source, using a combination of on-chain and + /// off-chain solutions. type IdentityProvider: IdentityProvider; + /// Customizable external logic to handle events in which a new identity + /// commitment is generated or removed. type ProviderHooks: ProviderHooks; + /// The aggregate `Event` type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; } + /// The pallet contains a single storage element, the `IdentityCommitments` + /// double map. Its first key is the `Identifier` of subjects, while the + /// second key is the commitment version. The values are identity + /// commitments. #[pallet::storage] #[pallet::getter(fn identity_commitments)] pub type IdentityCommitments = StorageDoubleMap< @@ -74,27 +124,42 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { + /// A new commitment has been stored. VersionedIdentityCommitted { + /// The identifier of the identity committed. identifier: T::Identifier, + /// The value of the commitment. commitment: IdentityCommitmentOf, + /// The version of the commitment. version: IdentityCommitmentVersion, }, + /// A commitment has been deleted. VersionedIdentityDeleted { + /// The identifier of the identity committed. identifier: T::Identifier, + /// The version of the commitment. version: IdentityCommitmentVersion, }, } #[pallet::error] pub enum Error { + /// The specified commitment cannot be found. CommitmentNotFound, + /// Error when retrieving the identity details of the provided subject. IdentityProvider(u16), + /// Error when generating a commitment for the retrieved identity. IdentityCommitmentGenerator(u16), + /// Error inside the external hook logic. Hook(u16), } #[pallet::call] impl Pallet { + /// Generate a new versioned commitment for the subject identified by + /// the provided `Identifier`. If an old commitment for the same version + /// is present, it is overridden. Hooks are called before the new + /// commitment is stored, and optionally before the old one is replaced. #[pallet::call_index(0)] // TODO: Update weight #[pallet::weight(0)] @@ -140,6 +205,10 @@ pub mod pallet { Ok(()) } + /// Delete an identity commitment of a specific version for a specific + /// `Identifier`. If a commitment of the provided version does not exist + /// for the given `Identifier`, an error is returned. Hooks are called + /// after the commitment has been removed. #[pallet::call_index(1)] // TODO: Update weight #[pallet::weight(0)] diff --git a/pallets/pallet-relay-store/Cargo.toml b/pallets/pallet-relay-store/Cargo.toml index c5398f052f..a27eab69bd 100644 --- a/pallets/pallet-relay-store/Cargo.toml +++ b/pallets/pallet-relay-store/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true homepage.workspace = true license-file.workspace = true name = "pallet-relay-store" -readme.workspace = true +readme = "README.md" repository.workspace = true version.workspace = true diff --git a/pallets/pallet-relay-store/README.md b/pallets/pallet-relay-store/README.md new file mode 100644 index 0000000000..e69de29bb2 From cf9a2e45c9ffee327fd8130ec608f12df54b9c1a Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Thu, 16 Nov 2023 10:50:27 +0100 Subject: [PATCH 02/17] Rust docs for traits --- pallets/pallet-dip-provider/src/traits.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/pallets/pallet-dip-provider/src/traits.rs b/pallets/pallet-dip-provider/src/traits.rs index 8cf5c09146..e2b117e750 100644 --- a/pallets/pallet-dip-provider/src/traits.rs +++ b/pallets/pallet-dip-provider/src/traits.rs @@ -27,6 +27,9 @@ pub mod identity_provision { use sp_std::marker::PhantomData; + /// A trait to retrieve identity information for a given identifier. The + /// information can come from a variety of different sources, as this pallet + /// does not impose any restrictions on that. pub trait IdentityProvider where Runtime: Config, @@ -34,10 +37,13 @@ pub mod identity_provision { type Error: Into; type Success; + /// Return the identity information for the identifier, if found. + /// Otherwise, return an error. fn retrieve(identifier: &Runtime::Identifier) -> Result; } - // Return the `Default` value if `Identity` adn `Details` both implement it. + /// Return the `Default` value of the provided `Identity` type if it + /// implements the `Default` trait. pub struct DefaultIdentityProvider(PhantomData); impl IdentityProvider for DefaultIdentityProvider @@ -64,6 +70,8 @@ pub mod identity_generation { use scale_info::TypeInfo; use sp_std::{fmt::Debug, marker::PhantomData}; + /// A trait to generate an identity commitment of a given version for some + /// identity info retrieved by the [`IdentityProvider`]. pub trait IdentityCommitmentGenerator where Runtime: Config, @@ -72,6 +80,8 @@ pub mod identity_generation { type Error: Into; type Output: Clone + Eq + Debug + TypeInfo + FullCodec + MaxEncodedLen; + /// Return the identity commitment for the given version and identity + /// information. fn generate_commitment( identifier: &Runtime::Identifier, identity: &IdentityOf, @@ -79,8 +89,8 @@ pub mod identity_generation { ) -> Result; } - // Implement the `IdentityCommitmentGenerator` by returning the `Default` value - // for the `Output` type. + /// Implement the [`IdentityCommitmentGenerator`] trait by returning the + /// `Default` value for the `Output` type. pub struct DefaultIdentityCommitmentGenerator(PhantomData); impl IdentityCommitmentGenerator for DefaultIdentityCommitmentGenerator @@ -101,6 +111,8 @@ pub mod identity_generation { } } +/// A trait for types that, among other things, contain information about the +/// submitter of a tx. pub trait SubmitterInfo { type Submitter; @@ -126,6 +138,8 @@ where } } +/// Hooks for additional customizable logic to be executed when new identity +/// commitments are stored or old ones are removed. pub trait ProviderHooks where Runtime: Config, @@ -147,6 +161,7 @@ where ) -> Result<(), Self::Error>; } +/// Implement the [`ProviderHooks`] trait with noops. pub struct NoopHooks; impl ProviderHooks for NoopHooks From e87a8202a5894fae5e4fe3bbb3c710c73ca06639 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Thu, 16 Nov 2023 14:12:28 +0100 Subject: [PATCH 03/17] Add comments for dip-consumer --- pallets/pallet-dip-consumer/README.md | 60 ++++++++++ pallets/pallet-dip-consumer/src/identity.rs | 43 ------- pallets/pallet-dip-consumer/src/lib.rs | 126 +++++++++++++++++--- pallets/pallet-dip-consumer/src/origin.rs | 8 ++ pallets/pallet-dip-consumer/src/traits.rs | 14 ++- pallets/pallet-dip-provider/README.md | 6 +- 6 files changed, 193 insertions(+), 64 deletions(-) delete mode 100644 pallets/pallet-dip-consumer/src/identity.rs diff --git a/pallets/pallet-dip-consumer/README.md b/pallets/pallet-dip-consumer/README.md index e69de29bb2..2cb51d7120 100644 --- a/pallets/pallet-dip-consumer/README.md +++ b/pallets/pallet-dip-consumer/README.md @@ -0,0 +1,60 @@ +# Decentralized Identity Provider (DIP) provider consumer pallet + +This pallet is a core component of the Decentralized Identity Provider protocol. +It enables entities with an identity on a connected Substrate-based chain (provider) to use those identities on the chain this pallet is deployed (consumers) without requiring those entities to set up a new identity locally. +A consumer chain is *connected* to a provider if there is a way for the consumer chain to verify state proofs about parts of the state of the provider chain. + +A cross-chain transaction with DIP assumes the entity submitting the transaction has already generated a cross-chain identity commitment on the provider chain, by interacting with the DIP provider pallet on the provider chain. +With a generated identity commitment, a cross-chain transaction flow for a generic entity `A` works as follows: + +1. `A` generates a state proof proving the state of the identity commitment on the provider chain. +2. `A` generates any additional information required for an identity proof to be successfully verified by the consumer runtime. +3. `A`, using their account `AccC` on the consumer chain, calls the `dispatch_as` extrinsic by providing its identifier on the provider chain, the generated proof, and the `Call` to be dispatched on the consumer chain. + 1. This pallet verifies if the proof is correct, if not returns an error. + 2. This pallet dispatches the provided `Call` with a new origin created by this pallet, returning any errors the dispatch action returns. The origin contains the information revealed in the proof, the identifier of the acting subject and the account `AccC` dispatching the transaction. + +The pallet is agnostic over the chain-specific definition of *identity proof verifier* and *identifier*, although, when deployed, they must be configured to respect the definition of identity and identity commitment established by the provider this pallet is linked to. + +For instance, if the provider establishes that an identity commitment is a Merkle root of a set of public keys, an identity proof for the consumer will most likely be a Merkle proof revealing a subset of those keys. +Similarly, if the provider defines an identity commitment as some ZK-commitment, the respective identity proof on the consumer chain will be a ZK-proof verifying the validity of the commitment and therefore of the revealed information. + +For identifiers, if the provider establishes that an identifier is a public key, the same definition must be used in the consumer pallet. +Other definitions for an identifier, such as a simple integer or a [Decentralized Identifier (DID)](https://www.w3.org/TR/did-core/), must also be configured in the same way. + +The pallet allows the consumer runtime to define some `LocalIdentityInfo` associated with each identifier, which the pallet's proof verifier can access and optionally modify upon proof verification. +Any changes made to the `LocalIdentityInfo` will be persisted if the identity proof is verified correctly and the extrinsic executed successfully. + +If the consumer does not need to store anything in addition to the information an identity proof conveys, they can simply use an empty tuple `()` for the local identity info. +Another example could be the use of signatures, which requires nonce to avoid replay protections. +In this case, a simple numeric type such as a `u64` or a `u128` could be used, and bumped by the proof verifies when validating each new cross-chain transaction proof. + +## The `Config` trait + +Being chain-agnostic, most of the runtime configurations must be passed to the pallet's `Config` trait. +Nevertheless, most of the types provided must reflect the definition of identity and identity commitment that the identity provider chain has established. +The trait has the following components: + +* `type DipCallOriginFilter: Contains>`: A preliminary filter that checks whether a provided `Call` accepts a DIP origin or not. If a call such as a system call does not accept a DIP origin, there is no need to verify the identity proof, hence the execution can bail out early. This does not guarantee that the dispatch call will succeed, but rather than it will mostly not fail with a `BadOrigin` error. +* `type DispatchOriginCheck: EnsureOrigin<::RuntimeOrigin, Success = Self::AccountId>`: The origin check on the `dispatch_as` extrinsic to verify that the caller is authorized to call the extrinsic. If successful, the check must return a `AccountId` as defined by the consumer runtime. +* `type Identifier: Parameter + MaxEncodedLen`: The type of a subject identifier. This must match the definition of `Identifier` the identity provider has defined in their deployment of the provider pallet. +* `type LocalIdentityInfo: FullCodec + TypeInfo + MaxEncodedLen`: Any additional information that must be available only to the provider runtime that is required to provide additional context when verifying a cross-chain identity proof. +* `type ProofVerifier: IdentityProofVerifier`: The core component of this pallet. It takes care of validating an identity proof and optionally update any `LocalIdentityInfo`. It also defines, via its associated type, the structure of the identity proof that must be passed to the `dispatch_as` extrinsic. Although not directly, the proof structure depends on the information that goes into the identity commitment on the provider chain, as that defines what information can be revealed as part of the commitment proof. Additional info to satisfy requirements according to the `LocalIdentityInfo` (e.g., a signature) must also be provided in the proof. +* `type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin>`: The aggregated `Call` type. +* `type RuntimeOrigin: From> + From<::RuntimeOrigin>`: The aggregated `Origin` type, which must include the origin exposed by this pallet. + +## Storage + +The pallet contains a single storage element, the `IdentityEntries` map. +It maps from a subject `Identifier` to an instance of `LocalIdentityInfo`. + +This information is updated by the proof verifier whenever a new cross-chain transaction and its proof is submitted. + +## Origin + +Because the pallet allows other `Call`s to be dispatched after an identity proof has been verified, it also exposes a `Origin` that can be used for those calls that require indeed a call to be DIP-authorized. + +The origin is created after the identity proof has been successfully verified by the proof verifier, and it includes the identifier of the subject, the address of the tx submitter, and the result returned by the proof verifier upon successful verification. + +## Calls + +0. `pub fn dispatch_as(origin: OriginFor, identifier: T::Identifier, proof: IdentityProofOf, call: Box>) -> DispatchResult`: Try to dispatch a new local call only if it passes all the DIP requirements. Specifically, the call will be dispatched if it passes the preliminary `DipCallOriginFilter` and if the proof verifier returns a `Ok(verification_result)` value. The value is then added to the `DipOrigin` and passed down as the origin for the specified `Call`. If the whole execution terminates successfully, any changes applied to the `LocalIdentityInfo` by the proof verifier are persisted to the pallet storage. diff --git a/pallets/pallet-dip-consumer/src/identity.rs b/pallets/pallet-dip-consumer/src/identity.rs deleted file mode 100644 index eed39f68d4..0000000000 --- a/pallets/pallet-dip-consumer/src/identity.rs +++ /dev/null @@ -1,43 +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 frame_support::RuntimeDebug; -use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; - -/// The identity entry for any given user that uses the DIP protocol. -#[derive(Encode, Decode, MaxEncodedLen, Default, TypeInfo, RuntimeDebug)] -pub struct IdentityDetails { - /// The identity digest information, typically used to verify identity - /// proofs. - pub digest: Digest, - /// The details related to the user, stored in the pallet storage. - pub details: Details, -} - -impl From for IdentityDetails -where - Details: Default, -{ - fn from(value: Digest) -> Self { - Self { - digest: value, - details: Details::default(), - } - } -} diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index 0c90942b23..a99493209b 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -16,11 +16,70 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -// TODO: Pallet description +//! This pallet is a core component of the Decentralized Identity Provider +//! protocol. It enables entities with an identity on a connected +//! Substrate-based chain (provider) to use those identities on the chain this +//! pallet is deployed (consumers) without requiring those entities to set up a +//! new identity locally. A consumer chain is *connected* to a provider if there +//! is a way for the consumer chain to verify state proofs about parts of the +//! state of the provider chain. + +//! A cross-chain transaction with DIP assumes the entity submitting the +//! transaction has already generated a cross-chain identity commitment on the +//! provider chain, by interacting with the DIP provider pallet on the provider +//! chain. With a generated identity commitment, a cross-chain transaction flow +//! for a generic entity `A` works as follows: + +//! 1. `A` generates a state proof proving the state of the identity commitment +//! on the provider chain. +//! 2. `A` generates any additional information required for an identity proof +//! to be successfully verified by the consumer runtime. +//! 3. `A`, using their account `AccC` on the consumer chain, calls the +//! `dispatch_as` extrinsic by providing its identifier on the provider +//! chain, the generated proof, and the `Call` to be dispatched on the +//! consumer chain. +//! 1. This pallet verifies if the proof is correct, if not returns an error. +//! 2. This pallet dispatches the provided `Call` with a new origin created +//! by this pallet, returning any errors the dispatch action returns. The +//! origin contains the information revealed in the proof, the identifier +//! of the acting subject and the account `AccC` dispatching the +//! transaction. + +//! The pallet is agnostic over the chain-specific definition of *identity proof +//! verifier* and *identifier*, although, when deployed, they must be configured +//! to respect the definition of identity and identity commitment established by +//! the provider this pallet is linked to. + +//! For instance, if the provider establishes that an identity commitment is a +//! Merkle root of a set of public keys, an identity proof for the consumer will +//! most likely be a Merkle proof revealing a subset of those keys. Similarly, +//! if the provider defines an identity commitment as some ZK-commitment, the +//! respective identity proof on the consumer chain will be a ZK-proof verifying +//! the validity of the commitment and therefore of the revealed information. + +//! For identifiers, if the provider establishes that an identifier is a public +//! key, the same definition must be used in the consumer pallet. Other definitions for an identifier, such as a simple integer or a [Decentralized Identifier (DID)](https://www.w3.org/TR/did-core/), must also be configured in the same way. + +//! The pallet allows the consumer runtime to define some `LocalIdentityInfo` +//! associated with each identifier, which the pallet's proof verifier can +//! access and optionally modify upon proof verification. Any changes made to +//! the `LocalIdentityInfo` will be persisted if the identity proof is verified +//! correctly and the extrinsic executed successfully. + +//! If the consumer does not need to store anything in addition to the +//! information an identity proof conveys, they can simply use an empty tuple +//! `()` for the local identity info. Another example could be the use of +//! signatures, which requires nonce to avoid replay protections. In this case, +//! a simple numeric type such as a `u64` or a `u128` could be used, and bumped +//! by the proof verifies when validating each new cross-chain transaction +//! proof. #![cfg_attr(not(feature = "std"), no_std)] -pub mod identity; +// !!! When Rust docs changes here, make sure to update the crate README.md file +// as well. +// !!! + pub mod traits; mod origin; @@ -50,33 +109,57 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); - #[pallet::storage] - #[pallet::getter(fn identity_proofs)] - pub(crate) type IdentityEntries = - StorageMap<_, Twox64Concat, ::Identifier, ::LocalIdentityInfo>; - #[pallet::config] pub trait Config: frame_system::Config { - /// Preliminary filter to filter out calls before doing any heavier - /// computations. + /// A preliminary filter that checks whether a provided `Call` accepts a + /// DIP origin or not. If a call such as a system call does not accept a + /// DIP origin, there is no need to verify the identity proof, hence the + /// execution can bail out early. This does not guarantee that the + /// dispatch call will succeed, but rather than it will mostly not fail + /// with a `BadOrigin` error. type DipCallOriginFilter: Contains>; - /// The origin check for the `dispatch_as` call. + /// The origin check on the `dispatch_as` extrinsic to verify that the + /// caller is authorized to call the extrinsic. If successful, the check + /// must return a `AccountId` as defined by the consumer runtime. type DispatchOriginCheck: EnsureOriginWithArg< ::RuntimeOrigin, Self::Identifier, Success = Self::AccountId, >; - /// The identifier of a subject, e.g., a DID. + /// The type of a subject identifier. This must match the definition of + /// `Identifier` the identity provider has defined in their deployment + /// of the provider pallet. type Identifier: Parameter + MaxEncodedLen; - /// The details stored in this pallet associated with any given subject. + /// Any additional information that must be available only to the + /// provider runtime that is required to provide additional context when + /// verifying a cross-chain identity proof. type LocalIdentityInfo: FullCodec + TypeInfo + MaxEncodedLen; - /// The logic of the proof verifier, called upon each execution of the - /// `dispatch_as` extrinsic. + /// The core component of this pallet. It takes care of validating an + /// identity proof and optionally update any `LocalIdentityInfo`. It + /// also defines, via its associated type, the structure of the identity + /// proof that must be passed to the `dispatch_as` extrinsic. Although + /// not directly, the proof structure depends on the information that + /// goes into the identity commitment on the provider chain, as that + /// defines what information can be revealed as part of the commitment + /// proof. Additional info to satisfy requirements according to the + /// `LocalIdentityInfo` (e.g., a signature) must also be provided in the + /// proof. type ProofVerifier: IdentityProofVerifier; + /// The aggregated `Call` type. type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin>; + /// The aggregated `Origin` type, which must include the origin exposed + /// by this pallet. type RuntimeOrigin: From> + From<::RuntimeOrigin>; } + /// The pallet contains a single storage element, the `IdentityEntries` map. + /// It maps from a subject `Identifier` to an instance of + /// `LocalIdentityInfo`. + #[pallet::storage] + #[pallet::getter(fn identity_proofs)] + pub(crate) type IdentityEntries = + StorageMap<_, Twox64Concat, ::Identifier, ::LocalIdentityInfo>; + #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); @@ -89,8 +172,10 @@ pub mod pallet { Filtered, } - /// The origin this pallet creates after a user has provided a valid - /// identity proof to dispatch other calls. + /// The origin is created after the identity proof has been successfully + /// verified by the proof verifier, and it includes the identifier of the + /// subject, the address of the tx submitter, and the result returned by the + /// proof verifier upon successful verification. #[pallet::origin] pub type Origin = DipOrigin<::Identifier, ::AccountId, VerificationResultOf>; @@ -98,7 +183,14 @@ pub mod pallet { // TODO: Benchmarking #[pallet::call] impl Pallet { - // TODO: Replace with a SignedExtra. + /// Try to dispatch a new local call only if it passes all the DIP + /// requirements. Specifically, the call will be dispatched if it passes + /// the preliminary `DipCallOriginFilter` and if the proof verifier + /// returns a `Ok(verification_result)` value. The value is then added + /// to the `DipOrigin` and passed down as the origin for the specified + /// `Call`. If the whole execution terminates successfully, any changes + /// applied to the `LocalIdentityInfo` by the proof verifier are + /// persisted to the pallet storage. #[pallet::call_index(0)] #[pallet::weight(0)] pub fn dispatch_as( diff --git a/pallets/pallet-dip-consumer/src/origin.rs b/pallets/pallet-dip-consumer/src/origin.rs index 415d055e0f..5281b25914 100644 --- a/pallets/pallet-dip-consumer/src/origin.rs +++ b/pallets/pallet-dip-consumer/src/origin.rs @@ -22,13 +22,21 @@ use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_std::marker::PhantomData; +/// An origin passed down to the to-be-dispatched `Call` upon successful DIP +/// proof verification. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct DipOrigin { + /// The subject identifier which is performing the DIP operation. pub identifier: Identifier, + /// The local account address of the tx submitter. pub account_address: AccountId, + /// Details returned by the proof verifier upon successful proof + /// verification. pub details: Details, } +/// Implementation of the `EnsureOrigin` trait verifying that a given origin is +/// a `DipOrigin`. pub struct EnsureDipOrigin(PhantomData<(Identifier, AccountId, Details)>); #[cfg(not(feature = "runtime-benchmarks"))] diff --git a/pallets/pallet-dip-consumer/src/traits.rs b/pallets/pallet-dip-consumer/src/traits.rs index 9213166288..bfc0bedee5 100644 --- a/pallets/pallet-dip-consumer/src/traits.rs +++ b/pallets/pallet-dip-consumer/src/traits.rs @@ -20,14 +20,25 @@ use frame_support::Parameter; use crate::{Config, RuntimeCallOf}; +/// A trait to verify a given DIP identity proof. The trait depends on the +/// runtime definition of the consumer pallet's `Identifier` and of the system +/// pallet's `AccountId`. The type of proof expected and the type returned upon +/// successful verification is defined as an associated type. pub trait IdentityProofVerifier where Runtime: Config, { + /// The error returned upon failed DIP proof verification. type Error: Into; + /// The accepted type for a DIP identity proof. type Proof: Parameter; + /// The type returned upon successful DIP proof verification. type VerificationResult; + /// Verify a given DIP proof given the calling context, including the call + /// being dispatched, the DIP subject dispatching it, the account submitting + /// the DIP tx, and the identity details of the DIP subject as stored in the + /// consumer pallet. fn verify_proof_for_call_against_details( call: &RuntimeCallOf, subject: &Runtime::Identifier, @@ -37,7 +48,8 @@ where ) -> Result; } -// Always returns success. +/// Dummy implementation of the [`IdentityProofVerifier`] trait which always +/// returns `Ok(())`. pub struct SuccessfulProofVerifier; impl IdentityProofVerifier for SuccessfulProofVerifier where diff --git a/pallets/pallet-dip-provider/README.md b/pallets/pallet-dip-provider/README.md index f486bcd63e..89d3ac942f 100644 --- a/pallets/pallet-dip-provider/README.md +++ b/pallets/pallet-dip-provider/README.md @@ -1,4 +1,4 @@ -# Decentralized Identity Provider (DIP) pallet +# Decentralized Identity Provider (DIP) provider pallet This pallet is a core component of the Decentralized Identity Provider protocol. It enables a Substrate-based chain (provider) to bridge the identities of its users to other connected chains (consumers) trustlessly. @@ -46,5 +46,5 @@ Similarly, the `VersionedIdentityDeleted`, is called whenever a commitment is de ## Calls -* `pub fn commit_identity(origin: OriginFor, identifier: T::Identifier, version: Option ) -> DispatchResult`: Generate a new versioned commitment for the subject identified by the provided `Identifier`. If an old commitment for the same version is present, it is overridden. Hooks are called before the new commitment is stored, and optionally before the old one is replaced. -* `pub fn delete_identity_commitment(origin: OriginFor, identifier: T::Identifier, version: Option) -> DispatchResult`: Delete an identity commitment of a specific version for a specific `Identifier`. If a commitment of the provided version does not exist for the given `Identifier`, an error is returned. Hooks are called after the commitment has been removed. +0. `pub fn commit_identity(origin: OriginFor, identifier: T::Identifier, version: Option ) -> DispatchResult`: Generate a new versioned commitment for the subject identified by the provided `Identifier`. If an old commitment for the same version is present, it is overridden. Hooks are called before the new commitment is stored, and optionally before the old one is replaced. +1. `pub fn delete_identity_commitment(origin: OriginFor, identifier: T::Identifier, version: Option) -> DispatchResult`: Delete an identity commitment of a specific version for a specific `Identifier`. If a commitment of the provided version does not exist for the given `Identifier`, an error is returned. Hooks are called after the commitment has been removed. From 5911596c650890cceae9cac938651a033470dd2c Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Thu, 16 Nov 2023 14:18:04 +0100 Subject: [PATCH 04/17] Add comments to pallet-relay-store pallet --- pallets/pallet-relay-store/src/lib.rs | 13 +++++++++++-- pallets/pallet-relay-store/src/relay.rs | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/pallets/pallet-relay-store/src/lib.rs b/pallets/pallet-relay-store/src/lib.rs index d5ca7bc1cf..957b0f762c 100644 --- a/pallets/pallet-relay-store/src/lib.rs +++ b/pallets/pallet-relay-store/src/lib.rs @@ -16,7 +16,10 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -// TODO: Pallet description +//! Pallet to store the last N (configurable) relay chain state roots to be used +//! for cross-chain state proof verification. The pallet relies on the +//! cumulus_parachain_system hook to populate the block `ValidationData` with +//! the latest relay chain state root. #![cfg_attr(not(feature = "std"), no_std)] @@ -36,18 +39,24 @@ pub mod pallet { const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + /// Maps from a relaychain block height to its related information, + /// including the state root. #[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. + /// Storage value complimentary to [`LatestRelayHeads`] implementing a FIFO + /// queue of the last N relay chain blocks info. #[pallet::storage] pub(crate) type LatestBlockHeights = StorageValue<_, BoundedVec, ValueQuery>; #[pallet::config] pub trait Config: frame_system::Config { + /// The maximum number of relaychain block details to store. When the + /// limit is reached, oldest blocks are overridden with new ones. #[pallet::constant] type MaxRelayBlocksStored: Get; } @@ -63,7 +72,7 @@ pub mod pallet { { fn on_initialize(_n: BlockNumberFor) -> Weight { // Reserve weight to update the last relay state root - // TODO: Replace with benchmarked version of `on_finalize(` + // TODO: Replace with benchmarked version of `on_finalize()` ::DbWeight::get().writes(2) } diff --git a/pallets/pallet-relay-store/src/relay.rs b/pallets/pallet-relay-store/src/relay.rs index 9dfaf69f95..1bca00ba53 100644 --- a/pallets/pallet-relay-store/src/relay.rs +++ b/pallets/pallet-relay-store/src/relay.rs @@ -20,7 +20,9 @@ use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_core::RuntimeDebug; +/// Information associated to a relaychain block. #[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug, MaxEncodedLen)] pub struct RelayParentInfo { + /// The relaychain block storage root. pub relay_parent_storage_root: Hash, } From eaed208f9759149f60886230acf5fd376a723443 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Thu, 16 Nov 2023 14:41:13 +0100 Subject: [PATCH 05/17] Add docs for the pallet-deposit-storage pallet --- pallets/pallet-deposit-storage/src/deposit.rs | 6 +++ pallets/pallet-deposit-storage/src/lib.rs | 50 ++++++++++++++++++- pallets/pallet-deposit-storage/src/traits.rs | 5 ++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/pallets/pallet-deposit-storage/src/deposit.rs b/pallets/pallet-deposit-storage/src/deposit.rs index c01296e23e..36b902df5b 100644 --- a/pallets/pallet-deposit-storage/src/deposit.rs +++ b/pallets/pallet-deposit-storage/src/deposit.rs @@ -32,12 +32,18 @@ use sp_std::marker::PhantomData; use crate::{BalanceOf, Config, Error, HoldReason, Pallet}; +/// Details associated to an on-chain deposit. #[derive(Clone, Debug, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo, MaxEncodedLen)] pub struct DepositEntry { + /// The [`Deposit`] entry. pub(crate) deposit: Deposit, + /// The `Reason` for the deposit. pub(crate) reason: Reason, } +/// Type implementing the [`DipProviderHooks`] hooks trait by taking a deposit +/// whenever an identity commitment is stored, and releasing the deposit +/// whenever an identity commitment is removed. pub struct FixedDepositCollectorViaDepositsPallet( PhantomData<(DepositsNamespace, FixedDepositAmount)>, ); diff --git a/pallets/pallet-deposit-storage/src/lib.rs b/pallets/pallet-deposit-storage/src/lib.rs index e683b27eac..ca38b5a438 100644 --- a/pallets/pallet-deposit-storage/src/lib.rs +++ b/pallets/pallet-deposit-storage/src/lib.rs @@ -16,8 +16,14 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +//! Pallet to store namespaced deposits for the configured `Currency`. It allows +//! the original payer of a deposit to claim it back, triggering a hook to +//! optionally perform related actions somewhere else in the runtime. +//! Each deposit is identified by a namespace and a key. There cannot be two +//! equal keys under the same namespace, but the same key can be present under +//! different namespaces. + #![cfg_attr(not(feature = "std"), no_std)] -#![recursion_limit = "256"] mod deposit; pub mod traits; @@ -58,17 +64,27 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { + /// The maximum length of keys. #[pallet::constant] type MaxKeyLength: Get; + /// The origin check, returning an `AccountId` upon completion, for who + /// can reclaim a deposit. type CheckOrigin: EnsureOrigin; + /// The currency from which deposits are to be taken. type Currency: Mutate; + /// Additional logic to execute whenever a new deposit a created or a + /// deposit is released. type DepositHooks: DepositStorageHooks; + /// The type of a deposit namespace. type Namespace: Parameter; + /// The aggregated `Event` type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// The aggregated `HoldReason` type. type RuntimeHoldReason: From + Clone + PartialEq + Debug + FullCodec + MaxEncodedLen + TypeInfo; } + /// The hold reasons for deposits taken by the pallet. #[pallet::composite_enum] pub enum HoldReason { Deposit, @@ -76,28 +92,45 @@ pub mod pallet { #[pallet::error] pub enum Error { + /// The deposit with the provided key was not found within the specified + /// namespace. DepositNotFound, + /// A deposit with the provided key already exists within the specified + /// namespace. DepositExisting, + /// The origin was not authorized to perform the operation on the + /// specified deposit entry. Unauthorized, + /// The external hook failed. Hook(u16), } #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { + /// A new deposit has been reserved and stored. DepositAdded { + /// The deposit namespace. namespace: T::Namespace, + /// The deposit key. key: DepositKeyOf, + /// The deposit details. deposit_entry: DepositEntryOf, }, + /// A deposit has been released and deleted from storage. DepositReclaimed { + /// The deposit namespace. namespace: T::Namespace, + /// The deposit key. key: DepositKeyOf, + /// The deposit details. deposit_entry: DepositEntryOf, }, } - // Double map (namespace, key) -> deposit + /// Storage of all deposits. Its first key is a namespace, and the second + /// one the deposit key. Its value include the details associated to a + /// deposit instance. #[pallet::storage] #[pallet::getter(fn deposits)] pub(crate) type Deposits = @@ -109,6 +142,10 @@ pub mod pallet { #[pallet::call] impl Pallet { + /// Reclaim a deposit that was previously taken. If there is no deposit + /// with the given key under the given namespace, it returns an error. + /// If a deposit exists, the deposit hooks are invoked after the deposit + /// has been removed from the pallet storage. #[pallet::call_index(0)] // TODO: Update weight #[pallet::weight(0)] @@ -122,6 +159,10 @@ pub mod pallet { } impl Pallet { + /// Add a deposit identified by the given key under the given namespace. + /// If there is already a deposit entry for the same key under the same + /// namespace, it returns an error. It also returns an error if the + /// deposit cannot be reserved on the pallet's `Currency`. pub fn add_deposit(namespace: T::Namespace, key: DepositKeyOf, entry: DepositEntryOf) -> DispatchResult { Deposits::::try_mutate(&namespace, &key, |deposit_entry| match deposit_entry { Some(_) => Err(DispatchError::from(Error::::DepositExisting)), @@ -143,6 +184,11 @@ pub mod pallet { Ok(()) } + /// Remove and release a deposit identified by the given key under the + /// given namespace. If there is no deposit with under the provided + /// namespace with the provided key, it returns an error. It also + /// returns an error if the deposit cannot be released on the pallet's + /// `Currency`. pub fn remove_deposit( namespace: &T::Namespace, key: &DepositKeyOf, diff --git a/pallets/pallet-deposit-storage/src/traits.rs b/pallets/pallet-deposit-storage/src/traits.rs index f514eee108..ba99b584a3 100644 --- a/pallets/pallet-deposit-storage/src/traits.rs +++ b/pallets/pallet-deposit-storage/src/traits.rs @@ -18,12 +18,16 @@ use crate::{Config, DepositEntryOf, DepositKeyOf}; +/// A trait to configure additional custom logic whenever a deposit-related +/// operation takes place. pub trait DepositStorageHooks where Runtime: Config, { type Error: Into; + /// Called by the pallet whenever a deposit for a given namespace and key is + /// removed. fn on_deposit_reclaimed( namespace: &Runtime::Namespace, key: &DepositKeyOf, @@ -31,6 +35,7 @@ where ) -> Result<(), Self::Error>; } +/// Dummy implementation of the [`DepositStorageHooks`] trait that does a noop. pub struct NoopDepositStorageHooks; impl DepositStorageHooks for NoopDepositStorageHooks From fa1906b02717aa5ec347470b1b2590a14461a49b Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Thu, 16 Nov 2023 17:18:26 +0100 Subject: [PATCH 06/17] wip kilt-dip-support docs --- crates/kilt-dip-support/src/lib.rs | 4 +- pallets/pallet-dip-consumer/src/lib.rs | 63 +------------------------- pallets/pallet-dip-provider/src/lib.rs | 33 +------------- pallets/pallet-relay-store/README.md | 0 4 files changed, 5 insertions(+), 95 deletions(-) delete mode 100644 pallets/pallet-relay-store/README.md diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-support/src/lib.rs index 0322801680..a2bbf75822 100644 --- a/crates/kilt-dip-support/src/lib.rs +++ b/crates/kilt-dip-support/src/lib.rs @@ -16,7 +16,9 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -// TODO: Crate documentation +//! Collection of support traits, types and functions for integrating KILT as an +//! identity provider following the Decentralized Identity Provider (DIP) +//! protocol. #![cfg_attr(not(feature = "std"), no_std)] diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index a99493209b..323239a915 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -16,69 +16,8 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -//! This pallet is a core component of the Decentralized Identity Provider -//! protocol. It enables entities with an identity on a connected -//! Substrate-based chain (provider) to use those identities on the chain this -//! pallet is deployed (consumers) without requiring those entities to set up a -//! new identity locally. A consumer chain is *connected* to a provider if there -//! is a way for the consumer chain to verify state proofs about parts of the -//! state of the provider chain. - -//! A cross-chain transaction with DIP assumes the entity submitting the -//! transaction has already generated a cross-chain identity commitment on the -//! provider chain, by interacting with the DIP provider pallet on the provider -//! chain. With a generated identity commitment, a cross-chain transaction flow -//! for a generic entity `A` works as follows: - -//! 1. `A` generates a state proof proving the state of the identity commitment -//! on the provider chain. -//! 2. `A` generates any additional information required for an identity proof -//! to be successfully verified by the consumer runtime. -//! 3. `A`, using their account `AccC` on the consumer chain, calls the -//! `dispatch_as` extrinsic by providing its identifier on the provider -//! chain, the generated proof, and the `Call` to be dispatched on the -//! consumer chain. -//! 1. This pallet verifies if the proof is correct, if not returns an error. -//! 2. This pallet dispatches the provided `Call` with a new origin created -//! by this pallet, returning any errors the dispatch action returns. The -//! origin contains the information revealed in the proof, the identifier -//! of the acting subject and the account `AccC` dispatching the -//! transaction. - -//! The pallet is agnostic over the chain-specific definition of *identity proof -//! verifier* and *identifier*, although, when deployed, they must be configured -//! to respect the definition of identity and identity commitment established by -//! the provider this pallet is linked to. - -//! For instance, if the provider establishes that an identity commitment is a -//! Merkle root of a set of public keys, an identity proof for the consumer will -//! most likely be a Merkle proof revealing a subset of those keys. Similarly, -//! if the provider defines an identity commitment as some ZK-commitment, the -//! respective identity proof on the consumer chain will be a ZK-proof verifying -//! the validity of the commitment and therefore of the revealed information. - -//! For identifiers, if the provider establishes that an identifier is a public -//! key, the same definition must be used in the consumer pallet. Other definitions for an identifier, such as a simple integer or a [Decentralized Identifier (DID)](https://www.w3.org/TR/did-core/), must also be configured in the same way. - -//! The pallet allows the consumer runtime to define some `LocalIdentityInfo` -//! associated with each identifier, which the pallet's proof verifier can -//! access and optionally modify upon proof verification. Any changes made to -//! the `LocalIdentityInfo` will be persisted if the identity proof is verified -//! correctly and the extrinsic executed successfully. - -//! If the consumer does not need to store anything in addition to the -//! information an identity proof conveys, they can simply use an empty tuple -//! `()` for the local identity info. Another example could be the use of -//! signatures, which requires nonce to avoid replay protections. In this case, -//! a simple numeric type such as a `u64` or a `u128` could be used, and bumped -//! by the proof verifies when validating each new cross-chain transaction -//! proof. - #![cfg_attr(not(feature = "std"), no_std)] - -// !!! When Rust docs changes here, make sure to update the crate README.md file -// as well. -// !!! +#![doc = include_str!("../README.md")] pub mod traits; diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index ca0819ddba..e9084e233d 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -16,39 +16,8 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -//! This pallet is a core component of the Decentralized Identity Provider -//! protocol. It enables a Substrate-based chain (provider) to bridge the -//! identities of its users to other connected chains (consumers) trustlessly. A -//! consumer chain is *connected* to a provider if there is a way for the -//! consumer chain to verify state proofs about parts of the state of the -//! provider chain. - -//! The pallet is agnostic over the chain-specific definition of *identity*, and -//! delegates the definition of it to the provider chain's runtime. - -//! What the pallet stores are *identity commitments*, which are opaque byte -//! blobs put in the pallet storage and on which the cross-chain identity -//! bridging protocol can be built. As for identities, the definition of an -//! identity commitment must be provided by the runtime and is therefore -//! provider-specific. Naturally, this definition must be made available to -//! consumers willing to integrate the identities living on the provider chain. - -//! Because providers and consumers evolve at different speeds, identity -//! commitments are versioned. This allows the provider chain to upgrade to a -//! newer commitment scheme, while still giving its users the possibility to use -//! the old version, if the chains on which they want to use their identity does -//! not yet support the new scheme. - -//! Identity commitments can be replaced (e.g., if something in the identity -//! info changes), or removed altogether by the identity subject. After removal, -//! the identity becomes unusable cross-chain, although it will still continue -//! to exist on the provider chain and will be usable for local operations. - #![cfg_attr(not(feature = "std"), no_std)] - -// !!! When Rust docs changes here, make sure to update the crate README.md file -// as well. -// !!! +#![doc = include_str!("../README.md")] pub mod traits; diff --git a/pallets/pallet-relay-store/README.md b/pallets/pallet-relay-store/README.md deleted file mode 100644 index e69de29bb2..0000000000 From 2d29592e96a356775d7b9ab6ae9a554b7e916f69 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Mon, 20 Nov 2023 10:06:33 +0100 Subject: [PATCH 07/17] Minor docs fixes after fix rebasing --- pallets/pallet-dip-provider/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/pallet-dip-provider/README.md b/pallets/pallet-dip-provider/README.md index 89d3ac942f..630e6be0fa 100644 --- a/pallets/pallet-dip-provider/README.md +++ b/pallets/pallet-dip-provider/README.md @@ -20,7 +20,7 @@ After removal, the identity becomes unusable cross-chain, although it will still Being chain-agnostic, most of the runtime configurations must be passed to the pallet's `Config` trait. Specifically: -* `type CommitOriginCheck: EnsureOrigin`: The check ensuring a runtime origin is allowed to generate and remove identity commitments. +* `type CommitOriginCheck: EnsureOrigin`: The check ensuring a given runtime origin is allowed to generate and remove identity commitments. * `type CommitOrigin: SubmitterInfo`: The resulting origin if `CommitOriginCheck` returns with errors. The origin is not required to be an `AccountId`, but must include information about the `AccountId` of the tx submitter. * `type Identifier: Parameter + MaxEncodedLen`: The type of an identifier used to retrieve identity information about a subject. * `type IdentityCommitmentGenerator: IdentityCommitmentGenerator`: The type responsible for generating identity commitments, given the identity information associated to a given `Identifier`. From 1ac05245fcec7e7be0791c0bb325daa391962d60 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Mon, 20 Nov 2023 11:04:15 +0100 Subject: [PATCH 08/17] Add rustdoc for DIP runtime-common --- runtimes/common/src/dip/README.md | 16 ++++++++++++++++ runtimes/common/src/dip/did.rs | 9 +++++++++ runtimes/common/src/dip/merkle.rs | 18 ++++++++++++++++++ runtimes/common/src/dip/mod.rs | 4 ++++ 4 files changed, 47 insertions(+) create mode 100644 runtimes/common/src/dip/README.md diff --git a/runtimes/common/src/dip/README.md b/runtimes/common/src/dip/README.md new file mode 100644 index 0000000000..4f60538e30 --- /dev/null +++ b/runtimes/common/src/dip/README.md @@ -0,0 +1,16 @@ +# KILT Decentralized Identity Provider (DIP) provider specification + +Specification of the format of a DIP identity commitment and the expected format of a DIP identity proof for cross-chain transactions using KILT identities. + +## V0 + +The V0 of the KILT DIP Provider specification defines the following components: + +* **Identity details**: What are the pieces of a KILT identity that can be used for cross-chain transactions. V0 defines them to include the following information: + * All `DidKey`s stored under the subject's DID Document. For more details about how these keys are defined, please check the [KILT DID pallet](../../../../pallets/did). + * All the `LinkableAccountId`s the DID subject has linked to the DID via the KILT linking pallet. For more details about how on-chain linking works, please check the [KILT lookup pallet](../../../../pallets/pallet-did-lookup/). + * (OPTIONAL) The web3name of the DID subject, if present. For more details about how web3names work, please check the [KILT web3name pallet](../../../../pallets/pallet-web3-names/). +* **Identity commitment**: Defines how the identity details above are aggregated into a value which will be selectively shared on a consumer chain for a cross-chain transaction. V0 defines the identity commitment as a Merkle root of all the elements above that uses the shame hashing algorithm as the runtime. Using a Merkle root allows the DID subject to generate proof that can selectively disclose different pieces of identity for different operations on different chains providing, among other things, better scalability for cases in which the linked information becomes large. The leaves encoded in the commitment can be of the following type: + * DID key leaf: with leaf name being the key ID, and leaf value being the key details as defined in the `DidPublicKeyDetails` type. + * Linked account leaf: with leaf name being the linked account ID, and leaf value being an empty tuple `()`. + * Web3name leaf: with leaf name being the web3name, and leaf value being the KILT block number in which it was linked to the DID. diff --git a/runtimes/common/src/dip/did.rs b/runtimes/common/src/dip/did.rs index 134c65de9b..9f9b7c581f 100644 --- a/runtimes/common/src/dip/did.rs +++ b/runtimes/common/src/dip/did.rs @@ -45,15 +45,24 @@ impl From for u16 { pub type Web3OwnershipOf = RevealedWeb3Name<::Web3Name, BlockNumberFor>; +/// Identity information related to a KILT DID relevant for cross-chain +/// transactions via the DIP protocol. pub struct LinkedDidInfoOf where Runtime: did::Config + pallet_web3_names::Config, { + /// The DID Document of the subject. pub did_details: DidDetails, + /// The optional web3name details linked to the subject. pub web3_name_details: Option>, + /// The list of accounts the subject has previously linked via the linking + /// pallet. pub linked_accounts: Vec, } +/// Type implementing the [`IdentityProvider`] trait which is responsible for +/// collecting the DID information relevant for DIP cross-chain transactions by +/// interacting with the different pallets involved. pub struct LinkedDidInfoProvider; impl IdentityProvider for LinkedDidInfoProvider diff --git a/runtimes/common/src/dip/merkle.rs b/runtimes/common/src/dip/merkle.rs index ec62b3d6a8..000740fd11 100644 --- a/runtimes/common/src/dip/merkle.rs +++ b/runtimes/common/src/dip/merkle.rs @@ -34,6 +34,8 @@ use kilt_dip_support::merkle::{DidKeyRelationship, RevealedDidMerkleProofLeaf}; use crate::dip::did::LinkedDidInfoOf; pub type BlindedValue = Vec; +/// Type of the Merkle proof revealing parts of the DIP identity of a given DID +/// subject. pub type DidMerkleProofOf = DidMerkleProof< Vec, RevealedDidMerkleProofLeaf< @@ -45,9 +47,13 @@ pub type DidMerkleProofOf = DidMerkleProof< >, >; +/// Type of a complete DIP Merkle proof. #[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] pub struct CompleteMerkleProof { + /// The Merkle root. pub root: Root, + /// The Merkle proof revealing parts of the commitment that verify against + /// the provided root. pub proof: Proof, } @@ -75,6 +81,7 @@ impl From for u16 { pub mod v0 { use super::*; + /// Type of a Merkle leaf revealed as part of a DIP Merkle proof. type ProofLeafOf = RevealedDidMerkleProofLeaf< KeyIdOf, ::AccountId, @@ -83,6 +90,8 @@ pub mod v0 { LinkableAccountId, >; + /// Given the provided DID info, it calculates the Merkle commitment (root) + /// using the provided in-memory DB. pub(super) fn calculate_root_with_db( identity: &LinkedDidInfoOf, db: &mut MemoryDB, @@ -232,6 +241,11 @@ pub mod v0 { Ok(trie_builder.root().to_owned()) } + /// Given the provided DID info, and a set of DID key IDs, account IDs and a + /// web3name, generates a Merkle proof that reveals only the provided + /// identity components. The function fails if no key or account with the + /// specified ID can be found, or if a web3name is requested to be revealed + /// in the proof but is not present in the provided identity details. pub(super) fn generate_proof<'a, Runtime, K, A>( identity: &LinkedDidInfoOf, key_ids: K, @@ -323,6 +337,7 @@ pub mod v0 { }) } + /// Given the provided DID info, generates a Merkle commitment (root). pub(super) fn generate_commitment( identity: &IdentityOf, ) -> Result @@ -335,6 +350,9 @@ pub mod v0 { } } +/// Type implementing the [`IdentityCommitmentGenerator`] and generating a +/// Merkle root of the provided identity details, according to the description +/// provided in the [README.md](./README.md), pub struct DidMerkleRootGenerator(PhantomData); impl IdentityCommitmentGenerator for DidMerkleRootGenerator diff --git a/runtimes/common/src/dip/mod.rs b/runtimes/common/src/dip/mod.rs index a3955297d6..9a578cec07 100644 --- a/runtimes/common/src/dip/mod.rs +++ b/runtimes/common/src/dip/mod.rs @@ -16,5 +16,9 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +#![doc = include_str!("./README.md")] + +/// Logic for collecting information related to a KILT DID. pub mod did; +/// Logic for generating Merkle commitments of a KILT DID identity. pub mod merkle; From 82e275b48fe3fcceccbbaa20317348e0eaf1537b Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Mon, 20 Nov 2023 11:28:09 +0100 Subject: [PATCH 09/17] Wip docs for kilt-dip-support crate --- crates/kilt-dip-support/src/did.rs | 32 +++++++++++++++++++++ crates/kilt-dip-support/src/merkle.rs | 2 ++ crates/kilt-dip-support/src/state_proofs.rs | 2 ++ 3 files changed, 36 insertions(+) diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index 7b16bcbb8e..acf9cdde71 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -16,6 +16,8 @@ // 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, @@ -32,15 +34,24 @@ use crate::{ traits::{Bump, DidSignatureVerifierContext, DipCallOriginFilter}, }; +/// 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 heigth. #[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, } @@ -62,6 +73,27 @@ impl From for u8 { } } +/// Proof verifier that tries to verify a DID signature over a given payload by +/// using one of the DID keys revealed in the Merkle proof. +/// The generic types indicate 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) struct RevealedDidKeysSignatureAndCallVerifier< Call, Submitter, diff --git a/crates/kilt-dip-support/src/merkle.rs b/crates/kilt-dip-support/src/merkle.rs index 2f23c952e5..957ec3beec 100644 --- a/crates/kilt-dip-support/src/merkle.rs +++ b/crates/kilt-dip-support/src/merkle.rs @@ -16,6 +16,8 @@ // 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}; diff --git a/crates/kilt-dip-support/src/state_proofs.rs b/crates/kilt-dip-support/src/state_proofs.rs index 0156143ce9..6feaa070d4 100644 --- a/crates/kilt-dip-support/src/state_proofs.rs +++ b/crates/kilt-dip-support/src/state_proofs.rs @@ -16,6 +16,8 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +//! Module to deal with cross-chain state proofs. + use parity_scale_codec::{Decode, Encode, HasCompact}; use sp_core::{storage::StorageKey, RuntimeDebug, U256}; use sp_runtime::generic::Header; From cb38ef840875339fd5ed2bb31e174f830ce1c8dc Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Mon, 20 Nov 2023 15:50:03 +0100 Subject: [PATCH 10/17] Comments complete and few renaming --- crates/kilt-dip-support/src/did.rs | 7 ++- crates/kilt-dip-support/src/export/child.rs | 12 ++-- crates/kilt-dip-support/src/export/sibling.rs | 8 +-- crates/kilt-dip-support/src/merkle.rs | 60 ++++++++++++++++--- crates/kilt-dip-support/src/state_proofs.rs | 34 ++++++++++- crates/kilt-dip-support/src/traits.rs | 60 +++++++++++++++++-- crates/kilt-dip-support/src/utils.rs | 1 + 7 files changed, 157 insertions(+), 25 deletions(-) diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index acf9cdde71..032e6cdb82 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -74,7 +74,10 @@ impl From for u8 { } /// Proof verifier that tries to verify a DID signature over a given payload by -/// using one of the DID keys revealed in the Merkle proof. +/// using one of the DID keys revealed in the Merkle proof. This verifier is to +/// typically be 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 indicate the following: /// * `Call`: The call to be dispatched on the local chain after verifying the /// DID signature. @@ -163,7 +166,7 @@ impl< (DidVerificationKey, DidVerificationKeyRelationship), RevealedDidKeysSignatureAndCallVerifierError, > { - let block_number = ContextProvider::block_number(); + 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) { diff --git a/crates/kilt-dip-support/src/export/child.rs b/crates/kilt-dip-support/src/export/child.rs index 6df44c2e49..b132758856 100644 --- a/crates/kilt-dip-support/src/export/child.rs +++ b/crates/kilt-dip-support/src/export/child.rs @@ -34,7 +34,7 @@ use crate::{ merkle::{DidMerkleProofVerifierError, RevealedDidMerkleProofLeaf, RevealedDidMerkleProofLeaves}, state_proofs::{parachain::DipIdentityCommitmentProofVerifierError, relay_chain::ParachainHeadProofVerifierError}, traits::{ - Bump, DidSignatureVerifierContext, DipCallOriginFilter, HistoricalBlockRegistry, ProviderParachainStateInfo, + Bump, DidSignatureVerifierContext, DipCallOriginFilter, HistoricalBlockRegistry, ProviderParachainStorageInfo, RelayChainStorageInfo, }, utils::OutputOf, @@ -346,8 +346,10 @@ impl< ChildProviderParachainId: Get, - ChildProviderStateInfo: - ProviderParachainStateInfo, + ChildProviderStateInfo: ProviderParachainStorageInfo< + Identifier = ConsumerRuntime::Identifier, + Commitment = ProviderDipMerkleHasher::Out, + >, OutputOf: Ord + From::Hasher>>, ChildProviderStateInfo::BlockNumber: Parameter + 'static, ChildProviderStateInfo::Commitment: Decode, @@ -457,7 +459,7 @@ mod v0 { }, traits::{ Bump, DidSignatureVerifierContext, DipCallOriginFilter, HistoricalBlockRegistry, - ProviderParachainStateInfo, RelayChainStorageInfo, + ProviderParachainStorageInfo, RelayChainStorageInfo, }, utils::OutputOf, }; @@ -559,7 +561,7 @@ mod v0 { ChildProviderParachainId: Get, - ChildProviderStateInfo: ProviderParachainStateInfo< + ChildProviderStateInfo: ProviderParachainStorageInfo< Identifier = ConsumerRuntime::Identifier, Commitment = ProviderDipMerkleHasher::Out, >, diff --git a/crates/kilt-dip-support/src/export/sibling.rs b/crates/kilt-dip-support/src/export/sibling.rs index 5f82560104..e7c86b7dc4 100644 --- a/crates/kilt-dip-support/src/export/sibling.rs +++ b/crates/kilt-dip-support/src/export/sibling.rs @@ -305,7 +305,7 @@ impl< SiblingProviderParachainId: Get, - SiblingProviderStateInfo: traits::ProviderParachainStateInfo< + SiblingProviderStateInfo: traits::ProviderParachainStorageInfo< Identifier = ConsumerRuntime::Identifier, Commitment = ProviderDipMerkleHasher::Out, >, @@ -403,7 +403,7 @@ mod v0 { export::common::v0::{DipMerkleProofAndDidSignature, ParachainRootStateProof}, merkle::DidMerkleProofVerifier, state_proofs::{parachain::DipIdentityCommitmentProofVerifier, relay_chain::ParachainHeadProofVerifier}, - traits::ProviderParachainStateInfo, + traits::ProviderParachainStorageInfo, }; #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] @@ -487,7 +487,7 @@ mod v0 { SiblingProviderParachainId: Get, - SiblingProviderStateInfo: traits::ProviderParachainStateInfo< + SiblingProviderStateInfo: traits::ProviderParachainStorageInfo< Identifier = ConsumerRuntime::Identifier, Commitment = ProviderDipMerkleHasher::Out, >, @@ -567,7 +567,7 @@ mod v0 { let proof_leaves: RevealedDidMerkleProofLeaves< ProviderDidKeyId, ProviderAccountId, - ::BlockNumber, + ::BlockNumber, ProviderWeb3Name, ProviderLinkedAccountId, MAX_REVEALED_KEYS_COUNT, diff --git a/crates/kilt-dip-support/src/merkle.rs b/crates/kilt-dip-support/src/merkle.rs index 957ec3beec..179a562830 100644 --- a/crates/kilt-dip-support/src/merkle.rs +++ b/crates/kilt-dip-support/src/merkle.rs @@ -26,6 +26,7 @@ use sp_runtime::{BoundedVec, SaturatedConversion}; use sp_std::{fmt::Debug, marker::PhantomData, 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, @@ -33,6 +34,7 @@ pub struct DidMerkleProof { pub revealed: Vec, } +/// Relationship of a key to a DID Document. #[derive(Clone, Copy, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord, MaxEncodedLen)] pub enum DidKeyRelationship { Encryption, @@ -57,6 +59,7 @@ impl TryFrom for DidVerificationKeyRelationship { } } +/// 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); @@ -65,7 +68,7 @@ impl From<(KeyId, DidKeyRelationship)> for DidKeyMerkleKey { 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); @@ -77,6 +80,7 @@ impl From> } } +/// 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); @@ -85,6 +89,7 @@ impl From for Web3NameMerkleKey { 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); @@ -94,6 +99,7 @@ impl From for Web3NameMerkleValue { } } +/// The key of a Merkle leaf revealing the an account linked to a DID Document. #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] pub struct LinkedAccountMerkleKey(pub AccountId); @@ -102,7 +108,8 @@ impl From for LinkedAccountMerkleKey { Self(value) } } - +/// The value of a Merkle leaf revealing the an account linked to a DID +/// Document. #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] pub struct LinkedAccountMerkleValue; @@ -112,10 +119,10 @@ impl From<()> for LinkedAccountMerkleValue { } } +/// 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 { - // 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), Web3Name(Web3NameMerkleKey, Web3NameMerkleValue), LinkedAccount(LinkedAccountMerkleKey, LinkedAccountMerkleValue), @@ -152,19 +159,32 @@ where } } +/// 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, @@ -175,8 +195,13 @@ pub struct RevealedDidMerkleProofLeaves< 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>, } @@ -220,9 +245,30 @@ impl From for u8 { } } -/// 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. +/// A type 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 indicate 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) struct DidMerkleProofVerifier< Hasher, KeyId, diff --git a/crates/kilt-dip-support/src/state_proofs.rs b/crates/kilt-dip-support/src/state_proofs.rs index 6feaa070d4..364017e233 100644 --- a/crates/kilt-dip-support/src/state_proofs.rs +++ b/crates/kilt-dip-support/src/state_proofs.rs @@ -87,6 +87,7 @@ mod substrate_no_std_port { } } +/// Relaychain-related state proof logic. pub(super) mod relay_chain { use super::*; @@ -113,6 +114,12 @@ pub(super) mod relay_chain { } } + /// Verifier of state proofs that reveal the value of a parachain head at a + /// given relaychain block number. + /// The generic types indicate the following: + /// * `RelayChainState`: defines the relaychain runtime types relevant for + /// state proof verification, and returns the relaychain runtime's storage + /// key identifying a parachain with a given ID. pub struct ParachainHeadProofVerifier(PhantomData); // Uses the provided `root` to verify the proof. @@ -123,6 +130,8 @@ pub(super) mod relay_chain { RelayChainState::BlockNumber: Copy + Into + TryFrom + HasCompact, RelayChainState::Key: AsRef<[u8]>, { + /// Given a relaychain state root, verify a state proof for the + /// parachain with the provided ID. pub fn verify_proof_for_parachain_with_root( para_id: &RelayChainState::ParaId, root: &OutputOf<::Hasher>, @@ -156,6 +165,9 @@ pub(super) mod relay_chain { RelayChainState::BlockNumber: Copy + Into + TryFrom + HasCompact, RelayChainState::Key: AsRef<[u8]>, { + /// Given a relaychain state root provided by the [`RelayChainState`] + /// generic type, verify a state proof for the parachain with the + /// provided ID. #[allow(clippy::result_unit_err)] pub fn verify_proof_for_parachain( para_id: &RelayChainState::ParaId, @@ -168,6 +180,12 @@ pub(super) mod relay_chain { } } + /// Implementor of the [`RelayChainStorageInfo`] trait that return the state + /// root of a relaychain block with a given number by retrieving it from the + /// [`pallet_relay_store::Pallet`] pallet storage. It hardcodes the + /// relaychain `BlockNumber`, `Hasher`, `StorageKey`, and `ParaId` to the + /// ones used by Polkadot-based relaychains. This type cannot be used with + /// relaychains that adopt a different definition for any on those types. pub struct RelayStateRootsViaRelayStorePallet(PhantomData); impl RelayChainStorageInfo for RelayStateRootsViaRelayStorePallet @@ -267,10 +285,11 @@ pub(super) mod relay_chain { } } +/// Parachain-related state proof logic. pub(super) mod parachain { use super::*; - use crate::traits::ProviderParachainStateInfo; + use crate::traits::ProviderParachainStorageInfo; #[derive(RuntimeDebug)] pub enum DipIdentityCommitmentProofVerifierError { @@ -289,15 +308,24 @@ pub(super) mod parachain { } } + /// Verifier of state proofs that reveal the value of the DIP commitment for + /// a given subject on the provider chain. The generic types indicate the + /// following: + /// * `ParaInfo`: defines the provider parachain runtime types relevant for + /// state proof verification, and returns the provider's runtime storage + /// key identifying the identity commitment for a subject with the given + /// identifier. pub struct DipIdentityCommitmentProofVerifier(PhantomData); impl DipIdentityCommitmentProofVerifier where - ParaInfo: ProviderParachainStateInfo, + ParaInfo: ProviderParachainStorageInfo, OutputOf: Ord, ParaInfo::Commitment: Decode, ParaInfo::Key: AsRef<[u8]>, { + /// Given a parachain state root, verify a state proof for the + /// commitment of a given subject identifier. #[allow(clippy::result_unit_err)] pub fn verify_proof_for_identifier( identifier: &ParaInfo::Identifier, @@ -339,7 +367,7 @@ pub(super) mod parachain { struct StaticSpiritnetInfoProvider; // We use the `system::eventCount()` storage entry as a unit test here. - impl ProviderParachainStateInfo for StaticSpiritnetInfoProvider { + impl ProviderParachainStorageInfo for StaticSpiritnetInfoProvider { type BlockNumber = u32; // The type of the `eventCount()` storage entry. type Commitment = u32; diff --git a/crates/kilt-dip-support/src/traits.rs b/crates/kilt-dip-support/src/traits.rs index aa426aae17..ae1c23af71 100644 --- a/crates/kilt-dip-support/src/traits.rs +++ b/crates/kilt-dip-support/src/traits.rs @@ -45,6 +45,8 @@ where /// A trait for types that implement some sort of access control logic on the /// provided input `Call` type. +/// The generic types indicate the following: +/// * `Call`: The type of the call being checked. pub trait DipCallOriginFilter { /// The error type for cases where the checks fail. type Error; @@ -54,35 +56,62 @@ pub trait DipCallOriginFilter { /// The success type for cases where the checks succeed. type Success; + /// Check whether the provided call can be dispatch with the given origin + /// information. fn check_call_origin_info(call: &Call, info: &Self::OriginInfo) -> Result; } +/// A trait that provides context (e.g., runtime type definitions, storage keys) +/// about the relaychain that is relevant for cross-chain state proofs. pub trait RelayChainStorageInfo { + /// The type of relaychain block numbers. type BlockNumber; + /// The type of the relaychain hashing algorithm. type Hasher: sp_runtime::traits::Hash; + /// The type of the relaychain storage key. type Key; + /// The type of parachain IDs. type ParaId; + /// Return the storage key pointing to the head of the parachain + /// identified by the provided ID. fn parachain_head_storage_key(para_id: &Self::ParaId) -> Self::Key; } +/// A trait that provides state information about specific relaychain blocks. pub trait RelayChainStateInfo: RelayChainStorageInfo { + /// Return the relaychain state root at a given block height. fn state_root_for_block(block_height: &Self::BlockNumber) -> Option>; } -pub trait ProviderParachainStateInfo { +/// A trait that provides context (e.g., runtime type definitions, storage keys) +/// about the DIP provider parachain that is relevant for cross-chain state +/// proofs. +pub trait ProviderParachainStorageInfo { + /// The type of the provider chain's block numbers. type BlockNumber; + /// The type of the provider chain's identity commitments. type Commitment; + /// The type of the provider chain's storage keys. type Key; + /// The type of the provider chain's hashing algorithm. type Hasher: sp_runtime::traits::Hash; + /// The type of the provider chain's identity subject identifiers. type Identifier; + /// Return the storage key pointing to the identity commitment for the given + /// identifier and version. fn dip_subject_storage_key(identifier: &Self::Identifier, version: IdentityCommitmentVersion) -> Self::Key; } +/// Implementation of the [`ProviderParachainStorageInfo`] trait that builds on +/// the definitions of a runtime that includes the DIP provider pallet (e.g., +/// KILT runtimes). +/// The generic types indicate the following: +/// * `T`: The runtime including the [`pallet_dip_provider::Pallet`] pallet. pub struct ProviderParachainStateInfoViaProviderPallet(PhantomData); -impl ProviderParachainStateInfo for ProviderParachainStateInfoViaProviderPallet +impl ProviderParachainStorageInfo for ProviderParachainStateInfoViaProviderPallet where T: pallet_dip_provider::Config, { @@ -99,18 +128,37 @@ where } } +/// A trait that provides the consumer parachain runtime additional context to +/// verify cross-chain DID signatures by subjects of the provider parachain. pub trait DidSignatureVerifierContext { + /// Max number of blocks a cross-chain DID signature is to be considered + /// fresh. const SIGNATURE_VALIDITY: u16; + /// The type of consumer parachain's block numbers. type BlockNumber; + /// The type of consumer parachain's hashes. type Hash; + /// Additional information that must be included in the payload being + /// DID-signed by the subject. type SignedExtra; - fn block_number() -> Self::BlockNumber; + /// Returns the block number of the consumer's chain in which the DID + /// signature is being evaluated. + fn current_block_number() -> Self::BlockNumber; + /// Returns the genesis hash of the consumer's chain. fn genesis_hash() -> Self::Hash; + /// Returns any additional info that must be appended to the payload before + /// verifying a cross-chain DID signature. fn signed_extra() -> Self::SignedExtra; } +/// Implementation of the [`DidSignatureVerifierContext`] trait that draws +/// information dynamically from the consumer's runtime using its system pallet. +/// The generic types indicate the following: +/// * `T`: The runtime including the [`frame_system::Pallet`] pallet. +/// * `SIGNATURE_VALIDITY`: The max number of blocks DID signatures are +/// considered valid. pub struct FrameSystemDidSignatureContext(PhantomData); impl DidSignatureVerifierContext @@ -124,7 +172,7 @@ where type Hash = T::Hash; type SignedExtra = (); - fn block_number() -> Self::BlockNumber { + fn current_block_number() -> Self::BlockNumber { frame_system::Pallet::::block_number() } @@ -135,10 +183,14 @@ where fn signed_extra() -> Self::SignedExtra {} } +/// A trait that provides access to information on historical blocks. pub trait HistoricalBlockRegistry { + /// The runtime definition of block numbers. type BlockNumber; + /// The runtime hashing algorithm. type Hasher: sp_runtime::traits::Hash; + /// Retrieve a block hash given its number. fn block_hash_for(block: &Self::BlockNumber) -> Option>; } diff --git a/crates/kilt-dip-support/src/utils.rs b/crates/kilt-dip-support/src/utils.rs index e382093b93..ac85bcdc93 100644 --- a/crates/kilt-dip-support/src/utils.rs +++ b/crates/kilt-dip-support/src/utils.rs @@ -16,4 +16,5 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +/// The output of a type implementing the [`sp_runtime::traits::Hash`] trait. pub type OutputOf = ::Output; From ececfff317d10f19201aeb4347c095da855b749b Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Mon, 20 Nov 2023 17:32:20 +0100 Subject: [PATCH 11/17] Template runtimes comments --- crates/kilt-dip-support/src/state_proofs.rs | 2 +- dip-template/runtimes/dip-consumer/src/dip.rs | 24 ++++++++++++++++ dip-template/runtimes/dip-consumer/src/lib.rs | 11 ++++++++ .../dip-consumer/src/origin_adapter.rs | 7 +++++ dip-template/runtimes/dip-provider/src/dip.rs | 28 +++++++++++++++++++ dip-template/runtimes/dip-provider/src/lib.rs | 6 ++++ runtime-api/dip-provider/src/lib.rs | 2 ++ 7 files changed, 79 insertions(+), 1 deletion(-) diff --git a/crates/kilt-dip-support/src/state_proofs.rs b/crates/kilt-dip-support/src/state_proofs.rs index 364017e233..eb1c1413a0 100644 --- a/crates/kilt-dip-support/src/state_proofs.rs +++ b/crates/kilt-dip-support/src/state_proofs.rs @@ -165,7 +165,7 @@ pub(super) mod relay_chain { RelayChainState::BlockNumber: Copy + Into + TryFrom + HasCompact, RelayChainState::Key: AsRef<[u8]>, { - /// Given a relaychain state root provided by the [`RelayChainState`] + /// Given a relaychain state root provided by the `RelayChainState` /// generic type, verify a state proof for the parachain with the /// provided ID. #[allow(clippy::result_unit_err)] diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index db25e19333..93ce9717f7 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -30,6 +30,10 @@ use sp_runtime::traits::BlakeTwo256; use crate::{AccountId, DidIdentifier, Runtime, RuntimeCall, RuntimeOrigin}; pub type MerkleProofVerifierOutput = >::VerificationResult; +/// The verifier logic assumes the provider is a sibling KILT parachain, and +/// that a KILT subject can provide DIP proof that reveal at most 10 DID keys +/// and 10 linked accounts. Calls that do not pass the [`DipCallFilter`] will be +/// discarded early on in the verification process. pub type ProofVerifier = KiltVersionedSiblingProviderVerifier< ProviderRuntime, ConstU32<2_000>, @@ -43,14 +47,22 @@ pub type ProofVerifier = KiltVersionedSiblingProviderVerifier< impl pallet_dip_consumer::Config for Runtime { type DipCallOriginFilter = PreliminaryDipOriginFilter; + // Any signed origin can submit a cross-chain DIP tx, since subject + // authentication (and optional binding to the tx submitter) is performed in the + // DIP proof verification step. type DispatchOriginCheck = EnsureSigned; type Identifier = DidIdentifier; + // Local identity info contains a simple `u128` representing a nonce. This means + // that two cross-chain operations targeting the same chain and having the same + // nonce cannot be both successfully evaluated. type LocalIdentityInfo = u128; type ProofVerifier = ProofVerifier; type RuntimeCall = RuntimeCall; type RuntimeOrigin = RuntimeOrigin; } +/// A preliminary DID call filter that only allows dispatching of extrinsics +/// from the [`pallet_postit::Pallet`] pallet. pub struct PreliminaryDipOriginFilter; impl Contains for PreliminaryDipOriginFilter { @@ -65,6 +77,9 @@ impl Contains for PreliminaryDipOriginFilter { } } +/// Calls to the [`pallet_postit::Pallet`] pallet or batches containing only +/// calls to the [`pallet_postit::Pallet`] pallet will go through if authorized +/// by a DID's authentication key. Everything else will fail. fn derive_verification_key_relationship(call: &RuntimeCall) -> Option { match call { RuntimeCall::PostIt { .. } => Some(DidVerificationKeyRelationship::Authentication), @@ -97,11 +112,18 @@ fn single_key_relationship<'a>( }) } +/// Errors generated by calls that do not pass the filter. pub enum DipCallFilterError { + /// The call cannot be dispatched with the provided origin. BadOrigin, + /// The call could be dispatched with the provided origin, but it has been + /// authorized with the wrong DID key. WrongVerificationRelationship, } +/// A call filter that requires calls to the [`pallet_postit::Pallet`] pallet to +/// be authorized with a DID signature generated with a key of a given +/// verification relationship. pub struct DipCallFilter; impl DipCallOriginFilter for DipCallFilter { @@ -122,5 +144,7 @@ impl DipCallOriginFilter for DipCallFilter { } impl pallet_relay_store::Config for Runtime { + // The pallet stores the last 100 relaychain state roots, making state proofs + // valid for at most 100 * 6 = 600 seconds. 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 953b179b9b..fbaf952028 100644 --- a/dip-template/runtimes/dip-consumer/src/lib.rs +++ b/dip-template/runtimes/dip-consumer/src/lib.rs @@ -16,6 +16,17 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +//! Runtime template of a Decentralized Identity Provider (DIP) consumer, which +//! does not itself include any identity-related pallets, but only the +//! [`pallet_dip_consumer::Pallet`] pallet (configured to work with the +//! [`dip_provider_runtime_template::Runtime`] template runtime), the +//! [`pallet_relay_store::Pallet`] pallet to keep track of finalized relaychain +//! state roots, and the example [`pallet_postit::Pallet`], which allows any +//! entity that can be identified with a username (e.g., a web3name carried over +//! from the provider chain) to post a message on chain, reply to another +//! on-chain message (including another reply), or like a message and/or any of +//! its replies. + #![cfg_attr(not(feature = "std"), no_std)] #![recursion_limit = "256"] diff --git a/dip-template/runtimes/dip-consumer/src/origin_adapter.rs b/dip-template/runtimes/dip-consumer/src/origin_adapter.rs index d973367b33..56cc3874d7 100644 --- a/dip-template/runtimes/dip-consumer/src/origin_adapter.rs +++ b/dip-template/runtimes/dip-consumer/src/origin_adapter.rs @@ -24,6 +24,11 @@ use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_core::RuntimeDebug; +/// An origin adapter which is used to make sure that a given [`DipOrigin`] +/// contains, among other things, a web3name. If a pallet extrinsic that +/// requires this origin is called with a DIP proof that does not revealed the +/// web3name linked to the subject, the extrinsic will fail with a `BadOrigin` +/// error. pub struct EnsureDipOriginAdapter; impl EnsureOrigin for EnsureDipOriginAdapter { @@ -40,6 +45,8 @@ impl EnsureOrigin for EnsureDipOriginAdapter { } } +/// A wrapper around a [`DipOrigin`] that makes sure the origin has a web3name, +/// or else the origin is invalid. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct DipOriginAdapter(DipOrigin); diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index 91c52a8c1b..622a8e0236 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -37,12 +37,21 @@ use crate::{ pub mod runtime_api { use super::*; + /// Parameters for a DIP proof request. #[derive(Encode, Decode, TypeInfo)] pub struct DipProofRequest { + /// The subject identifier for which to generate the DIP proof. pub(crate) identifier: DidIdentifier, + /// The DIP version. pub(crate) version: IdentityCommitmentVersion, + /// The DID key IDs of the subject's DID Document to reveal in the DIP + /// proof. pub(crate) keys: Vec>, + /// The list of accounts linked to the subject's DID to reveal in the + /// DIP proof. pub(crate) accounts: Vec, + /// A flag indicating whether the web3name claimed by the DID subject + /// should revealed in the DIP proof. pub(crate) should_include_web3_name: bool, } @@ -70,6 +79,8 @@ pub mod deposit { DipProvider, } + /// The namespace to use in the [`pallet_deposit_storage::Pallet`] to store + /// all deposits related to DIP commitments. pub struct DipProviderDepositNamespace; impl Get for DipProviderDepositNamespace { @@ -78,8 +89,11 @@ pub mod deposit { } } + /// The amount of tokens locked for each identity commitment. pub const DEPOSIT_AMOUNT: Balance = 2 * UNIT; + /// The additional logic to execute whenever a deposit is removed by its + /// owner directly via the [`pallet_deposit_storage::Pallet`] pallet. pub type DepositCollectorHooks = FixedDepositCollectorViaDepositsPallet>; @@ -97,6 +111,11 @@ pub mod deposit { } } + /// The logic to execute whenever an identity commitment is generated and + /// stored in the [`pallet_dip_provider::Pallet`] pallet. + /// + /// Upon storing and removing identity commitments, this hook will reserve + /// or release deposits from the [`pallet_deposit_storage::Pallet`] pallet. pub struct DepositHooks; impl DepositStorageHooks for DepositHooks { @@ -126,7 +145,10 @@ pub mod deposit { } impl pallet_deposit_storage::Config for Runtime { + // Any signed origin can submit the tx, which will go through only if the + // deposit payer matches the signed origin. type CheckOrigin = EnsureSigned; + // The balances pallet is used to reserve/unreserve tokens. type Currency = Balances; type DepositHooks = DepositHooks; type MaxKeyLength = ConstU32<256>; @@ -136,10 +158,16 @@ impl pallet_deposit_storage::Config for Runtime { } impl pallet_dip_provider::Config for Runtime { + // Only DID origins can submit the commitment identity tx, which will go through + // only if the DID in the origin matches the identifier specified in the tx. type CommitOriginCheck = EnsureDidOrigin; type CommitOrigin = DidRawOrigin; type Identifier = DidIdentifier; + // The identity commitment is defined as the Merkle root of the linked identity + // info, as specified by the [`LinkedDidInfoProvider`]. type IdentityCommitmentGenerator = DidMerkleRootGenerator; + // Identity info is defined as the collection of DID keys, linked accounts, and + // the optional web3name of a given DID subject. type IdentityProvider = LinkedDidInfoProvider; type ProviderHooks = deposit::DepositCollectorHooks; type RuntimeEvent = RuntimeEvent; diff --git a/dip-template/runtimes/dip-provider/src/lib.rs b/dip-template/runtimes/dip-provider/src/lib.rs index a0f474b817..5a6b498dac 100644 --- a/dip-template/runtimes/dip-provider/src/lib.rs +++ b/dip-template/runtimes/dip-provider/src/lib.rs @@ -16,6 +16,12 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +//! Runtime template of a Decentralized Identity Provider (DIP) provider, which +//! includes, beyond system pallets, [`did::Pallet`], +//! [`pallet_web3_names::Pallet`], and [`pallet_did_lookup::Pallet`] pallets, as +//! well as the [`pallet_dip_provider::Pallet`] pallet and the +//! [`pallet_deposit_storage::Pallet`] pallet. + #![cfg_attr(not(feature = "std"), no_std)] #![recursion_limit = "256"] diff --git a/runtime-api/dip-provider/src/lib.rs b/runtime-api/dip-provider/src/lib.rs index b910f734c1..1b7b8170da 100644 --- a/runtime-api/dip-provider/src/lib.rs +++ b/runtime-api/dip-provider/src/lib.rs @@ -21,11 +21,13 @@ use parity_scale_codec::Codec; sp_api::decl_runtime_apis! { + /// Runtime API to generate a DIP proof with the provided parameters. pub trait DipProvider where ProofRequest: Codec, Success: Codec, Error: Codec, { + /// Generate a DIP proof with the parameters specified in the request. fn generate_proof(request: ProofRequest) -> Result; } } From 1763f2901f8f1b0aadd5f54e66bcacbd7b7640ee Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Tue, 21 Nov 2023 11:19:35 +0100 Subject: [PATCH 12/17] Finalize comments --- crates/kilt-dip-support/src/export/child.rs | 142 +++++++++++++++++- crates/kilt-dip-support/src/export/common.rs | 5 + crates/kilt-dip-support/src/export/mod.rs | 2 + crates/kilt-dip-support/src/export/sibling.rs | 138 ++++++++++++++++- crates/kilt-dip-support/src/lib.rs | 4 + 5 files changed, 276 insertions(+), 15 deletions(-) diff --git a/crates/kilt-dip-support/src/export/child.rs b/crates/kilt-dip-support/src/export/child.rs index b132758856..fe970fce5a 100644 --- a/crates/kilt-dip-support/src/export/child.rs +++ b/crates/kilt-dip-support/src/export/child.rs @@ -41,6 +41,10 @@ use crate::{ FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet, }; +/// A KILT-specific DIP identity proof for a parent consumer that supports +/// versioning. +/// +/// For more info, refer to the version-specific proofs. #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] #[non_exhaustive] pub enum VersionedChildParachainDipStateProof< @@ -118,8 +122,49 @@ where } } -// Implements the same `IdentityProvider` trait, but it is internally configured -// by receiving the runtime definitions of both the provider and the receiver. +/// Proof verifier configured given a specific KILT runtime implementation. +/// +/// It is a specialization of the +/// [`GenericVersionedDipChildProviderStateProofVerifier`] type, with +/// configurations derived from the provided KILT runtime. +/// +/// The generic types +/// indicate the following: +/// * `KiltRuntime`: A KILT runtime definition. +/// * `KiltParachainId`: The ID of the specific KILT parachain instance. +/// * `RelayChainInfo`: The type providing information about the consumer +/// (relay)chain. +/// * `KiltDipMerkleHasher`: The hashing algorithm used by the KILT parachain +/// for the generation of the DIP identity commitment. +/// * `LocalDidCallVerifier`: Logic to map `RuntimeCall`s to a specific DID key +/// relationship. This information is used once the Merkle proof is verified, +/// to filter only the revealed keys that match the provided relationship. +/// * `MAX_REVEALED_KEYS_COUNT`: Max number of DID keys that the verifier will +/// accept being revealed as part of the DIP identity proof. +/// * `MAX_REVEALED_ACCOUNTS_COUNT`: Max number of linked accounts that the +/// verifier will accept being revealed as part of the DIP identity proof. +/// * `MAX_DID_SIGNATURE_DURATION`: Max number of blocks a cross-chain DID +/// signature is considered fresh. +/// +/// It specializes the [`GenericVersionedDipChildProviderStateProofVerifier`] +/// type by using the following types for its generics: +/// * `RelayChainInfo`: The provided `RelayChainInfo`. +/// * `ChildProviderParachainId`: The provided `KiltParachainId`. +/// * `ChildProviderStateInfo`: The +/// [`ProviderParachainStateInfoViaProviderPallet`] type configured with the +/// provided `KiltRuntime`. +/// * `ProviderDipMerkleHasher`: The provided `KiltDipMerkleHasher`. +/// * `ProviderDidKeyId`: The [`KeyIdOf`] type configured with the provided +/// `KiltRuntime`. +/// * `ProviderAccountId`: The `KiltRuntime::AccountId` type. +/// * `ProviderWeb3Name`: The `KiltRuntime::Web3Name` type. +/// * `ProviderLinkedAccountId`: The [`LinkableAccountId`] type. +/// * `MAX_REVEALED_KEYS_COUNT`: The provided `MAX_REVEALED_KEYS_COUNT`. +/// * `MAX_REVEALED_ACCOUNTS_COUNT`: The provided `MAX_REVEALED_ACCOUNTS_COUNT`. +/// * `LocalContextProvider`: The [`FrameSystemDidSignatureContext`] type +/// configured with the provided `KiltRuntime` and +/// `MAX_DID_SIGNATURE_DURATION`. +/// * `LocalDidCallVerifier`: The provided `LocalDidCallVerifier`. pub struct KiltVersionedChildProviderVerifier< KiltRuntime, KiltParachainId, @@ -258,10 +303,15 @@ impl< } } -// More generic version compared to `VersionedChildKiltProviderVerifier`, to be -// used in cases in which it is not possible or not desirable to depend on the -// whole provider runtime definition. Hence, required types must be filled in -// manually. +/// Generic proof verifier for KILT-specific DIP identity proofs of different +/// versions coming from a child provider running one of the available KILT +/// runtimes. +/// +/// It expects the DIP proof to be a [`VersionedChildParachainDipStateProof`], +/// and returns [`RevealedDidMerkleProofLeaves`] if the proof is successfully +/// verified. +/// +/// For more info, refer to the version-specific proof identifiers. pub struct GenericVersionedDipChildProviderStateProofVerifier< RelayChainInfo, ChildProviderParachainId, @@ -433,7 +483,7 @@ pub mod latest { pub use super::v0::ChildParachainDipStateProof; } -mod v0 { +pub mod v0 { use super::*; use parity_scale_codec::Codec; @@ -464,6 +514,10 @@ mod v0 { utils::OutputOf, }; + /// The expected format of a cross-chain DIP identity proof when the + /// identity information is being bridged from a provider that is a child of + /// the chain where the information is being consumed (i.e., consumer + /// chain). #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub struct ChildParachainDipStateProof< ParentBlockHeight: Copy + Into + TryFrom, @@ -471,12 +525,85 @@ mod v0 { DipMerkleProofBlindedValues, DipMerkleProofRevealedLeaf, > { + /// The state proof for the given parachain head. para_state_root: ParachainRootStateProof, + /// The relaychain header for the relaychain block specified in the + /// `para_state_root`. relay_header: Header, + /// The raw state proof for the DIP commitment of the given subject. dip_identity_commitment: Vec>, + /// The cross-chain DID signature. did: DipMerkleProofAndDidSignature, } + /// Generic proof verifier for KILT-specific DIP identity proofs coming from + /// a child provider running one of the available KILT runtimes. + /// The proof verification step is performed on every request, and this + /// specific verifier has no knowledge of caching or storing state about the + /// subject. It only takes the provided + /// `ConsumerRuntime::LocalIdentityInfo` and bumps it up if the proof is + /// successfully verified, to prevent replay attacks. If additional logic is + /// to be stored under the `ConsumerRuntime::LocalIdentityInfo` entry, a + /// different verifier or a wrapper around this verifier must be built. + /// + /// It expects the DIP proof to be a + /// [`VersionedChildParachainDipStateProof`], and returns + /// [`RevealedDidMerkleProofLeaves`] if the proof is successfully verified. + /// This information is then made availabe as an origin to the downstream + /// call being dispatched. + /// + /// The verifier performs the following steps: + /// 1. Verifies the state proof about the state root of the relaychain block + /// at the provided height. The state root is retrieved from the provided + /// relaychain header, which is checked to be the header of a + /// previously-finalized relaychain block. + /// 2. Verifies the state proof about the DIP commitment value on the + /// provider parachain at the block finalized at the given relaychain + /// block, using the relay state root validated in the previous step. + /// 3. Verifies the DIP Merkle proof revealing parts of the subject's DID + /// Document against the retrieved DIP commitment validated in the + /// previous step. + /// 4. Verifies the cross-chain DID signature over the payload composed by + /// the SCALE-encoded tuple of `(C, D, S, B, G, E)`, with: + /// * `C`: The `RuntimeCall` to dispatch after performing DIP + /// verification. + /// * `D`: The local details associated to the DID subject as stored in + /// the [`pallet_dip_consumer`] `IdentityEntries` storage map. + /// * `S`: The tx submitter's address. + /// * `B`: The block number of the consumer chain provided in the + /// cross-chain DID signature. + /// * `G`: The genesis hash of the consumer chain. + /// * `E`: Any additional information provided by the + /// `LocalContextProvider` implementation. + /// The generic types + /// indicate the following: + /// * `RelayChainInfo`: The type providing information about the consumer + /// (relay)chain. + /// * `ChildProviderParachainId`: The parachain ID of the provider KILT + /// child parachain. + /// * `ChildProviderStateInfo`: The type providing storage and state + /// information about the provider KILT child parachain. + /// * `ProviderDipMerkleHasher`: The hashing algorithm used by the KILT + /// parachain for the generation of the DIP identity commitment. + /// * `ProviderDidKeyId`: The runtime type of a DID key ID as defined by the + /// KILT child parachain. + /// * `ProviderAccountId`: The runtime type of an account ID as defined by + /// the KILT child parachain. + /// * `ProviderWeb3Name`: The runtime type of a web3name as defined by the + /// KILT child parachain. + /// * `ProviderLinkedAccountId`: The runtime type of a linked account ID as + /// defined by the KILT child parachain. + /// * `MAX_REVEALED_KEYS_COUNT`: Max number of DID keys that the verifier + /// will accept being revealed as part of the DIP identity proof. + /// * `MAX_REVEALED_ACCOUNTS_COUNT`: Max number of linked accounts that the + /// verifier will accept being revealed as part of the DIP identity proof. + /// * `LocalContextProvider`: The type providing context of the consumer + /// chain (e.g., current block number) for the sake of cross-chain DID + /// signature verification. + /// * `LocalDidCallVerifier`: Logic to map `RuntimeCall`s to a specific DID + /// key relationship. This information is used once the Merkle proof is + /// verified, to filter only the revealed keys that match the provided + /// relationship. pub struct DipChildProviderStateProofVerifier< RelayChainInfo, ChildProviderParachainId, @@ -631,7 +758,6 @@ mod v0 { // 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( diff --git a/crates/kilt-dip-support/src/export/common.rs b/crates/kilt-dip-support/src/export/common.rs index d0eff66d68..0248ecb860 100644 --- a/crates/kilt-dip-support/src/export/common.rs +++ b/crates/kilt-dip-support/src/export/common.rs @@ -30,13 +30,18 @@ pub mod v0 { #[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo, Clone)] pub struct ParachainRootStateProof { + /// The relaychain block height for which the proof has been generated. pub(crate) relay_block_height: RelayBlockHeight, + /// The raw state proof. pub(crate) proof: Vec>, } #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] pub struct DipMerkleProofAndDidSignature { + /// The DIP Merkle proof revealing some leaves about the DID subject's + /// identity. pub(crate) leaves: DidMerkleProof, + /// The cross-chain DID signature. pub(crate) signature: TimeBoundDidSignature, } } diff --git a/crates/kilt-dip-support/src/export/mod.rs b/crates/kilt-dip-support/src/export/mod.rs index 8d2f289926..01fd5ad2a1 100644 --- a/crates/kilt-dip-support/src/export/mod.rs +++ b/crates/kilt-dip-support/src/export/mod.rs @@ -16,7 +16,9 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +/// Verification logic to integrate a child chain as a DIP provider. pub mod child; +/// Verification logic to integrate a sibling chain as a DIP provider. pub mod sibling; mod common; diff --git a/crates/kilt-dip-support/src/export/sibling.rs b/crates/kilt-dip-support/src/export/sibling.rs index e7c86b7dc4..c7c1d7c5ab 100644 --- a/crates/kilt-dip-support/src/export/sibling.rs +++ b/crates/kilt-dip-support/src/export/sibling.rs @@ -38,6 +38,10 @@ use crate::{ FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet, }; +/// A KILT-specific DIP identity proof for a sibling consumer that supports +/// versioning. +/// +/// For more info, refer to the version-specific proofs. #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] #[non_exhaustive] pub enum VersionedSiblingParachainDipStateProof< @@ -110,8 +114,49 @@ where } } } -// Implements the same `IdentityProvider` trait, but it is internally configured -// by receiving the runtime definitions of both the provider and the receiver. + +/// Proof verifier configured given a specific KILT runtime implementation. +/// +/// It is a specialization of the +/// [`GenericVersionedDipSiblingProviderStateProofVerifier`] type, with +/// configurations derived from the provided KILT runtime. +/// +/// The generic types +/// indicate the following: +/// * `KiltRuntime`: A KILT runtime definition. +/// * `KiltParachainId`: The ID of the specific KILT parachain instance. +/// * `RelayChainInfo`: The type providing information about the relaychain. +/// * `KiltDipMerkleHasher`: The hashing algorithm used by the KILT parachain +/// for the generation of the DIP identity commitment. +/// * `LocalDidCallVerifier`: Logic to map `RuntimeCall`s to a specific DID key +/// relationship. This information is used once the Merkle proof is verified, +/// to filter only the revealed keys that match the provided relationship. +/// * `MAX_REVEALED_KEYS_COUNT`: Max number of DID keys that the verifier will +/// accept being revealed as part of the DIP identity proof. +/// * `MAX_REVEALED_ACCOUNTS_COUNT`: Max number of linked accounts that the +/// verifier will accept being revealed as part of the DIP identity proof. +/// * `MAX_DID_SIGNATURE_DURATION`: Max number of blocks a cross-chain DID +/// signature is considered fresh. +/// +/// It specializes the [`GenericVersionedDipSiblingProviderStateProofVerifier`] +/// type by using the following types for its generics: +/// * `RelayChainInfo`: The provided `RelayChainInfo`. +/// * `ChildProviderParachainId`: The provided `KiltParachainId`. +/// * `ChildProviderStateInfo`: The +/// [`ProviderParachainStateInfoViaProviderPallet`] type configured with the +/// provided `KiltRuntime`. +/// * `ProviderDipMerkleHasher`: The provided `KiltDipMerkleHasher`. +/// * `ProviderDidKeyId`: The [`KeyIdOf`] type configured with the provided +/// `KiltRuntime`. +/// * `ProviderAccountId`: The `KiltRuntime::AccountId` type. +/// * `ProviderWeb3Name`: The `KiltRuntime::Web3Name` type. +/// * `ProviderLinkedAccountId`: The [`LinkableAccountId`] type. +/// * `MAX_REVEALED_KEYS_COUNT`: The provided `MAX_REVEALED_KEYS_COUNT`. +/// * `MAX_REVEALED_ACCOUNTS_COUNT`: The provided `MAX_REVEALED_ACCOUNTS_COUNT`. +/// * `LocalContextProvider`: The [`FrameSystemDidSignatureContext`] type +/// configured with the provided `KiltRuntime` and +/// `MAX_DID_SIGNATURE_DURATION`. +/// * `LocalDidCallVerifier`: The provided `LocalDidCallVerifier`. pub struct KiltVersionedSiblingProviderVerifier< KiltRuntime, KiltParachainId, @@ -233,10 +278,15 @@ impl< } } -// More generic version compared to `VersionedSiblingKiltProviderVerifier`, to -// be used in cases in which it is not possible or not desirable to depend on -// the whole provider runtime definition. Hence, required types must be filled -// in manually. +/// Generic proof verifier for KILT-specific DIP identity proofs of different +/// versions coming from a sibling provider running one of the available KILT +/// runtimes. +/// +/// It expects the DIP proof to be a [`VersionedSiblingParachainDipStateProof`], +/// and returns [`RevealedDidMerkleProofLeaves`] if the proof is successfully +/// verified. +/// +/// For more info, refer to the version-specific proof identifiers. pub struct GenericVersionedDipSiblingProviderStateProofVerifier< RelayChainStateInfo, SiblingProviderParachainId, @@ -392,7 +442,7 @@ pub mod latest { pub use super::v0::SiblingParachainDipStateProof; } -mod v0 { +pub mod v0 { use super::*; use frame_support::Parameter; @@ -406,6 +456,10 @@ mod v0 { traits::ProviderParachainStorageInfo, }; + /// The expected format of a cross-chain DIP identity proof when the + /// identity information is being bridged from a provider that is a sibling + /// of the chain where the information is being consumed (i.e., consumer + /// chain). #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] pub struct SiblingParachainDipStateProof< RelayBlockHeight, @@ -413,11 +467,81 @@ mod v0 { DipMerkleProofRevealedLeaf, LocalBlockNumber, > { + /// The state proof for the given parachain head. para_state_root: ParachainRootStateProof, + /// The raw state proof for the DIP commitment of the given subject. dip_identity_commitment: Vec>, + /// The cross-chain DID signature. did: DipMerkleProofAndDidSignature, } + /// Generic proof verifier for KILT-specific DIP identity proofs coming from + /// a sibling provider running one of the available KILT runtimes. + /// + /// The proof verification step is performed on every request, and this + /// specific verifier has no knowledge of caching or storing state about the + /// subject. It only takes the provided + /// `ConsumerRuntime::LocalIdentityInfo` and bumps it up if the proof is + /// successfully verified, to prevent replay attacks. If additional logic is + /// to be stored under the `ConsumerRuntime::LocalIdentityInfo` entry, a + /// different verifier or a wrapper around this verifier must be built. + /// + /// It expects the DIP proof to be a + /// [`VersionedSiblingParachainDipStateProof`], and returns + /// [`RevealedDidMerkleProofLeaves`] if the proof is successfully verified. + /// This information is then made availabe as an origin to the downstream + /// call being dispatched. + /// + /// The verifier performs the following steps: + /// 1. Verifies the state proof about the state root of the relaychain block + /// at the provided height. The state root is provided by the + /// `RelayChainInfo` type. + /// 2. Verifies the state proof about the DIP commitment value on the + /// provider parachain at the block finalized at the given relaychain + /// block, using the relay state root validated in the previous step. + /// 3. Verifies the DIP Merkle proof revealing parts of the subject's DID + /// Document against the retrieved DIP commitment validated in the + /// previous step. + /// 4. Verifies the cross-chain DID signature over the payload composed by + /// the SCALE-encoded tuple of `(C, D, S, B, G, E)`, with: + /// * `C`: The `RuntimeCall` to dispatch after performing DIP + /// verification. + /// * `D`: The local details associated to the DID subject as stored in + /// the [`pallet_dip_consumer`] `IdentityEntries` storage map. + /// * `S`: The tx submitter's address. + /// * `B`: The block number of the consumer chain provided in the + /// cross-chain DID signature. + /// * `G`: The genesis hash of the consumer chain. + /// * `E`: Any additional information provided by the + /// `LocalContextProvider` implementation. + /// The generic types + /// indicate the following: + /// * `RelayChainInfo`: The type providing information about the relaychain. + /// * `SiblingProviderParachainId`: The parachain ID of the provider KILT + /// sibling parachain. + /// * `SiblingProviderStateInfo`: The type providing storage and state + /// information about the provider KILT sibling parachain. + /// * `ProviderDipMerkleHasher`: The hashing algorithm used by the KILT + /// parachain for the generation of the DIP identity commitment. + /// * `ProviderDidKeyId`: The runtime type of a DID key ID as defined by the + /// KILT child parachain. + /// * `ProviderAccountId`: The runtime type of an account ID as defined by + /// the KILT child parachain. + /// * `ProviderWeb3Name`: The runtime type of a web3name as defined by the + /// KILT child parachain. + /// * `ProviderLinkedAccountId`: The runtime type of a linked account ID as + /// defined by the KILT child parachain. + /// * `MAX_REVEALED_KEYS_COUNT`: Max number of DID keys that the verifier + /// will accept being revealed as part of the DIP identity proof. + /// * `MAX_REVEALED_ACCOUNTS_COUNT`: Max number of linked accounts that the + /// verifier will accept being revealed as part of the DIP identity proof. + /// * `LocalContextProvider`: The type providing context of the consumer + /// chain (e.g., current block number) for the sake of cross-chain DID + /// signature verification. + /// * `LocalDidCallVerifier`: Logic to map `RuntimeCall`s to a specific DID + /// key relationship. This information is used once the Merkle proof is + /// verified, to filter only the revealed keys that match the provided + /// relationship. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub struct DipSiblingProviderStateProofVerifier< RelayChainStateInfo, diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-support/src/lib.rs index a2bbf75822..95b9b82ef4 100644 --- a/crates/kilt-dip-support/src/lib.rs +++ b/crates/kilt-dip-support/src/lib.rs @@ -19,6 +19,10 @@ //! Collection of support traits, types and functions for integrating KILT as an //! identity provider following the Decentralized Identity Provider (DIP) //! protocol. +//! +//! Consumers of KILT identities should prefer using directly +//! [`KiltVersionedChildProviderVerifier`] for consumer relaychains and +//! [`KiltVersionedSiblingProviderVerifier`] for consumer sibling parachains. #![cfg_attr(not(feature = "std"), no_std)] From f113e45377fa6abe4a680c1c9227f47003f5b97c Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Fri, 24 Nov 2023 10:39:33 +0100 Subject: [PATCH 13/17] Remove unnecessary clippy ignores --- crates/kilt-dip-support/src/did.rs | 1 - crates/kilt-dip-support/src/merkle.rs | 1 - crates/kilt-dip-support/src/state_proofs.rs | 2 -- 3 files changed, 4 deletions(-) diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index 032e6cdb82..b971f3e91c 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -156,7 +156,6 @@ impl< CallVerifier: DipCallOriginFilter, DidVerificationKeyRelationship)>, { - #[allow(clippy::result_unit_err)] pub(crate) fn verify_did_signature_for_call( call: &Call, submitter: &Submitter, diff --git a/crates/kilt-dip-support/src/merkle.rs b/crates/kilt-dip-support/src/merkle.rs index 179a562830..24ff808b5e 100644 --- a/crates/kilt-dip-support/src/merkle.rs +++ b/crates/kilt-dip-support/src/merkle.rs @@ -307,7 +307,6 @@ impl< LinkedAccountId: Encode + Clone, Web3Name: Encode + Clone, { - #[allow(clippy::result_unit_err)] #[allow(clippy::type_complexity)] pub(crate) fn verify_dip_merkle_proof( identity_commitment: &Hasher::Out, diff --git a/crates/kilt-dip-support/src/state_proofs.rs b/crates/kilt-dip-support/src/state_proofs.rs index eb1c1413a0..81560785ac 100644 --- a/crates/kilt-dip-support/src/state_proofs.rs +++ b/crates/kilt-dip-support/src/state_proofs.rs @@ -168,7 +168,6 @@ pub(super) mod relay_chain { /// Given a relaychain state root provided by the `RelayChainState` /// generic type, verify a state proof for the parachain with the /// provided ID. - #[allow(clippy::result_unit_err)] pub fn verify_proof_for_parachain( para_id: &RelayChainState::ParaId, relay_height: &RelayChainState::BlockNumber, @@ -326,7 +325,6 @@ pub(super) mod parachain { { /// Given a parachain state root, verify a state proof for the /// commitment of a given subject identifier. - #[allow(clippy::result_unit_err)] pub fn verify_proof_for_identifier( identifier: &ParaInfo::Identifier, state_root: OutputOf, From f4e8bbfe2a0fb6d5be8fe2172ee8eb868e72716a Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 27 Nov 2023 09:31:26 +0000 Subject: [PATCH 14/17] feat: update templates and Dockerfile to support Zombienet (#587) Zombienet uses the empty chainspec for a parachain, and that was not supported by the node templates. Also, the Dockerfile assumes the `kilt-parachain` is being run, but this is different now, and could also be different in the future, so the binary target should be parametrized: anyway, it is not necessary to compile the whole workspace if only a specific binary is required. --- Dockerfile | 10 ++++++---- dip-template/nodes/dip-consumer/Cargo.toml | 3 +++ dip-template/nodes/dip-consumer/src/command.rs | 8 +++++--- dip-template/nodes/dip-provider/Cargo.toml | 3 +++ dip-template/nodes/dip-provider/src/command.rs | 8 +++++--- 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/Dockerfile b/Dockerfile index c64f906b8e..ace5ce348b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,19 +6,21 @@ FROM paritytech/ci-unified:bullseye-1.70.0 as builder WORKDIR /build ARG FEATURES=default +ARG BINARY=kilt-parachain COPY . . -RUN cargo build --locked --release --features $FEATURES +RUN cargo build --locked --release --features $FEATURES -p $BINARY # ===== SECOND STAGE ====== FROM docker.io/library/ubuntu:20.04 -LABEL description="This is the 2nd stage: a very small image where we copy the kilt-parachain binary." -ARG NODE_TYPE=kilt-parachain +ARG BINARY=kilt-parachain -COPY --from=builder /build/target/release/$NODE_TYPE /usr/local/bin/node-executable +LABEL description="This is the 2nd stage: a very small image where we copy the ${BINARY} binary." + +COPY --from=builder /build/target/release/$BINARY /usr/local/bin/node-executable RUN useradd -m -u 1000 -U -s /bin/sh -d /node node && \ mkdir -p /node/.local/share/node && \ diff --git a/dip-template/nodes/dip-consumer/Cargo.toml b/dip-template/nodes/dip-consumer/Cargo.toml index b48e55d0db..2937dee83f 100644 --- a/dip-template/nodes/dip-consumer/Cargo.toml +++ b/dip-template/nodes/dip-consumer/Cargo.toml @@ -68,3 +68,6 @@ cumulus-relay-chain-interface.workspace = true [build-dependencies] substrate-build-script-utils.workspace = true + +[features] +default = [] diff --git a/dip-template/nodes/dip-consumer/src/command.rs b/dip-template/nodes/dip-consumer/src/command.rs index e560839502..7d5809ceda 100644 --- a/dip-template/nodes/dip-consumer/src/command.rs +++ b/dip-template/nodes/dip-consumer/src/command.rs @@ -33,15 +33,17 @@ use sc_telemetry::TelemetryEndpoints; use sp_runtime::traits::AccountIdConversion; use crate::{ - chain_spec::{development_config, Extensions}, + chain_spec::{development_config, ChainSpec as ConsumerChainSpec, Extensions}, cli::{Cli, RelayChainCli, Subcommand}, service::{new_partial, start_parachain_node}, }; fn load_spec(id: &str) -> std::result::Result, String> { match id { - "dev" => Ok(Box::new(development_config())), - _ => Err("Unrecognized spec ID.".into()), + "dev" | "" => Ok(Box::new(development_config())), + path => Ok(Box::new(ConsumerChainSpec::from_json_file(std::path::PathBuf::from( + path, + ))?)), } } diff --git a/dip-template/nodes/dip-provider/Cargo.toml b/dip-template/nodes/dip-provider/Cargo.toml index 0bc866dddf..b9d134dbc7 100644 --- a/dip-template/nodes/dip-provider/Cargo.toml +++ b/dip-template/nodes/dip-provider/Cargo.toml @@ -68,3 +68,6 @@ cumulus-relay-chain-interface.workspace = true [build-dependencies] substrate-build-script-utils.workspace = true + +[features] +default = [] diff --git a/dip-template/nodes/dip-provider/src/command.rs b/dip-template/nodes/dip-provider/src/command.rs index 6e508956e0..ca4f66d478 100644 --- a/dip-template/nodes/dip-provider/src/command.rs +++ b/dip-template/nodes/dip-provider/src/command.rs @@ -33,15 +33,17 @@ use sc_telemetry::TelemetryEndpoints; use sp_runtime::traits::AccountIdConversion; use crate::{ - chain_spec::{development_config, Extensions}, + chain_spec::{development_config, ChainSpec as ProviderChainSpec, Extensions}, cli::{Cli, RelayChainCli, Subcommand}, service::{new_partial, start_parachain_node}, }; fn load_spec(id: &str) -> std::result::Result, String> { match id { - "dev" => Ok(Box::new(development_config())), - _ => Err("Unrecognized spec ID.".into()), + "dev" | "" => Ok(Box::new(development_config())), + path => Ok(Box::new(ProviderChainSpec::from_json_file(std::path::PathBuf::from( + path, + ))?)), } } From 758db1fda96a54a8c72255219400b0ac146c2c83 Mon Sep 17 00:00:00 2001 From: Chris Chinchilla Date: Fri, 1 Dec 2023 10:35:35 +0200 Subject: [PATCH 15/17] Update pallets/pallet-dip-consumer/README.md Co-authored-by: Antonio --- pallets/pallet-dip-consumer/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/pallet-dip-consumer/README.md b/pallets/pallet-dip-consumer/README.md index 2cb51d7120..b6360066e6 100644 --- a/pallets/pallet-dip-consumer/README.md +++ b/pallets/pallet-dip-consumer/README.md @@ -10,7 +10,7 @@ With a generated identity commitment, a cross-chain transaction flow for a gener 1. `A` generates a state proof proving the state of the identity commitment on the provider chain. 2. `A` generates any additional information required for an identity proof to be successfully verified by the consumer runtime. 3. `A`, using their account `AccC` on the consumer chain, calls the `dispatch_as` extrinsic by providing its identifier on the provider chain, the generated proof, and the `Call` to be dispatched on the consumer chain. - 1. This pallet verifies if the proof is correct, if not returns an error. + 1. This pallet verifies if the proof is correct, if not it returns an error. 2. This pallet dispatches the provided `Call` with a new origin created by this pallet, returning any errors the dispatch action returns. The origin contains the information revealed in the proof, the identifier of the acting subject and the account `AccC` dispatching the transaction. The pallet is agnostic over the chain-specific definition of *identity proof verifier* and *identifier*, although, when deployed, they must be configured to respect the definition of identity and identity commitment established by the provider this pallet is linked to. From 048de341761a921446cb96ab23b8467d5ba1f1eb Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 6 Dec 2023 12:12:04 +0100 Subject: [PATCH 16/17] Address PR review --- crates/kilt-dip-support/src/did.rs | 6 +++--- crates/kilt-dip-support/src/export/child.rs | 21 +++++++++---------- crates/kilt-dip-support/src/export/sibling.rs | 16 +++++++------- crates/kilt-dip-support/src/lib.rs | 6 +++--- crates/kilt-dip-support/src/merkle.rs | 6 +++--- crates/kilt-dip-support/src/state_proofs.rs | 2 +- crates/kilt-dip-support/src/traits.rs | 14 ++++++------- dip-template/runtimes/dip-consumer/src/dip.rs | 2 +- pallets/pallet-deposit-storage/src/lib.rs | 2 +- pallets/pallet-dip-consumer/README.md | 10 ++++----- pallets/pallet-dip-provider/README.md | 2 +- runtimes/common/src/dip/README.md | 6 +++--- 12 files changed, 46 insertions(+), 47 deletions(-) diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index c419635881..dfdce46dba 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -87,11 +87,11 @@ impl From for u8 { } /// Proof verifier 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 to -/// typically be used in conjunction with a verifier that takes a user-provided +/// 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 indicate the following: +/// 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) diff --git a/crates/kilt-dip-support/src/export/child.rs b/crates/kilt-dip-support/src/export/child.rs index 7f04bde15b..00a897b538 100644 --- a/crates/kilt-dip-support/src/export/child.rs +++ b/crates/kilt-dip-support/src/export/child.rs @@ -124,12 +124,11 @@ where /// Proof verifier configured given a specific KILT runtime implementation. /// -/// It is a specialization of the +/// A specialization of the /// [`GenericVersionedDipChildProviderStateProofVerifier`] type, with /// configurations derived from the provided KILT runtime. /// -/// The generic types -/// indicate the following: +/// The generic types are the following: /// * `KiltRuntime`: A KILT runtime definition. /// * `KiltParachainId`: The ID of the specific KILT parachain instance. /// * `RelayChainInfo`: The type providing information about the consumer @@ -140,9 +139,9 @@ where /// relationship. This information is used once the Merkle proof is verified, /// to filter only the revealed keys that match the provided relationship. /// * `MAX_REVEALED_KEYS_COUNT`: Max number of DID keys that the verifier will -/// accept being revealed as part of the DIP identity proof. +/// accept revealed as part of the DIP identity proof. /// * `MAX_REVEALED_ACCOUNTS_COUNT`: Max number of linked accounts that the -/// verifier will accept being revealed as part of the DIP identity proof. +/// verifier will accept revealed as part of the DIP identity proof. /// * `MAX_DID_SIGNATURE_DURATION`: Max number of blocks a cross-chain DID /// signature is considered fresh. /// @@ -515,8 +514,8 @@ pub mod v0 { }; /// The expected format of a cross-chain DIP identity proof when the - /// identity information is being bridged from a provider that is a child of - /// the chain where the information is being consumed (i.e., consumer + /// identity information is bridged from a provider that is a child of + /// the chain where the information is consumed (i.e., consumer /// chain). #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub struct ChildParachainDipStateProof< @@ -541,7 +540,7 @@ pub mod v0 { /// The proof verification step is performed on every request, and this /// specific verifier has no knowledge of caching or storing state about the /// subject. It only takes the provided - /// `ConsumerRuntime::LocalIdentityInfo` and bumps it up if the proof is + /// `ConsumerRuntime::LocalIdentityInfo` and increases it if the proof is /// successfully verified, to prevent replay attacks. If additional logic is /// to be stored under the `ConsumerRuntime::LocalIdentityInfo` entry, a /// different verifier or a wrapper around this verifier must be built. @@ -550,7 +549,7 @@ pub mod v0 { /// [`VersionedChildParachainDipStateProof`], and returns /// [`RevealedDidMerkleProofLeaves`] if the proof is successfully verified. /// This information is then made availabe as an origin to the downstream - /// call being dispatched. + /// call dispatched. /// /// The verifier performs the following steps: /// 1. Verifies the state proof about the state root of the relaychain block @@ -594,9 +593,9 @@ pub mod v0 { /// * `ProviderLinkedAccountId`: The runtime type of a linked account ID as /// defined by the KILT child parachain. /// * `MAX_REVEALED_KEYS_COUNT`: Max number of DID keys that the verifier - /// will accept being revealed as part of the DIP identity proof. + /// will accept revealed as part of the DIP identity proof. /// * `MAX_REVEALED_ACCOUNTS_COUNT`: Max number of linked accounts that the - /// verifier will accept being revealed as part of the DIP identity proof. + /// verifier will accept revealed as part of the DIP identity proof. /// * `LocalContextProvider`: The type providing context of the consumer /// chain (e.g., current block number) for the sake of cross-chain DID /// signature verification. diff --git a/crates/kilt-dip-support/src/export/sibling.rs b/crates/kilt-dip-support/src/export/sibling.rs index 808e0799eb..daf3759881 100644 --- a/crates/kilt-dip-support/src/export/sibling.rs +++ b/crates/kilt-dip-support/src/export/sibling.rs @@ -152,9 +152,9 @@ where /// relationship. This information is used once the Merkle proof is verified, /// to filter only the revealed keys that match the provided relationship. /// * `MAX_REVEALED_KEYS_COUNT`: Max number of DID keys that the verifier will -/// accept being revealed as part of the DIP identity proof. +/// accept revealed as part of the DIP identity proof. /// * `MAX_REVEALED_ACCOUNTS_COUNT`: Max number of linked accounts that the -/// verifier will accept being revealed as part of the DIP identity proof. +/// verifier will accept revealed as part of the DIP identity proof. /// * `MAX_DID_SIGNATURE_DURATION`: Max number of blocks a cross-chain DID /// signature is considered fresh. /// @@ -477,8 +477,8 @@ pub mod v0 { }; /// The expected format of a cross-chain DIP identity proof when the - /// identity information is being bridged from a provider that is a sibling - /// of the chain where the information is being consumed (i.e., consumer + /// identity information is bridged from a provider that is a sibling + /// of the chain where the information is consumed (i.e., consumer /// chain). #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] pub struct SiblingParachainDipStateProof< @@ -526,7 +526,7 @@ pub mod v0 { /// The proof verification step is performed on every request, and this /// specific verifier has no knowledge of caching or storing state about the /// subject. It only takes the provided - /// `ConsumerRuntime::LocalIdentityInfo` and bumps it up if the proof is + /// `ConsumerRuntime::LocalIdentityInfo` and increases it if the proof is /// successfully verified, to prevent replay attacks. If additional logic is /// to be stored under the `ConsumerRuntime::LocalIdentityInfo` entry, a /// different verifier or a wrapper around this verifier must be built. @@ -535,7 +535,7 @@ pub mod v0 { /// [`VersionedSiblingParachainDipStateProof`], and returns /// [`RevealedDidMerkleProofLeaves`] if the proof is successfully verified. /// This information is then made availabe as an origin to the downstream - /// call being dispatched. + /// call dispatched. /// /// The verifier performs the following steps: /// 1. Verifies the state proof about the state root of the relaychain block @@ -577,9 +577,9 @@ pub mod v0 { /// * `ProviderLinkedAccountId`: The runtime type of a linked account ID as /// defined by the KILT child parachain. /// * `MAX_REVEALED_KEYS_COUNT`: Max number of DID keys that the verifier - /// will accept being revealed as part of the DIP identity proof. + /// will accept revealed as part of the DIP identity proof. /// * `MAX_REVEALED_ACCOUNTS_COUNT`: Max number of linked accounts that the - /// verifier will accept being revealed as part of the DIP identity proof. + /// verifier will accept revealed as part of the DIP identity proof. /// * `LocalContextProvider`: The type providing context of the consumer /// chain (e.g., current block number) for the sake of cross-chain DID /// signature verification. diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-support/src/lib.rs index 85ff92bc1c..66ec155cdc 100644 --- a/crates/kilt-dip-support/src/lib.rs +++ b/crates/kilt-dip-support/src/lib.rs @@ -16,11 +16,11 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -//! Collection of support traits, types and functions for integrating KILT as an -//! identity provider following the Decentralized Identity Provider (DIP) +//! Collection of support traits, types, and functions for integrating KILT as +//! an identity provider following the Decentralized Identity Provider (DIP) //! protocol. //! -//! Consumers of KILT identities should prefer using directly +//! Consumers of KILT identities should prefer directly using //! [`KiltVersionedChildProviderVerifier`] for consumer relaychains and //! [`KiltVersionedSiblingProviderVerifier`] for consumer sibling parachains. diff --git a/crates/kilt-dip-support/src/merkle.rs b/crates/kilt-dip-support/src/merkle.rs index ef17a36237..ebe2826c8d 100644 --- a/crates/kilt-dip-support/src/merkle.rs +++ b/crates/kilt-dip-support/src/merkle.rs @@ -117,7 +117,7 @@ impl From for Web3NameMerkleValue { } } -/// The key of a Merkle leaf revealing the an account linked to a DID Document. +/// 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); @@ -126,7 +126,7 @@ impl From for LinkedAccountMerkleKey { Self(value) } } -/// The value of a Merkle leaf revealing the an account linked to a DID +/// 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; @@ -290,7 +290,7 @@ impl From for u8 { /// 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 indicate the following: +/// 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. diff --git a/crates/kilt-dip-support/src/state_proofs.rs b/crates/kilt-dip-support/src/state_proofs.rs index 8e0639c8aa..58619eb6bb 100644 --- a/crates/kilt-dip-support/src/state_proofs.rs +++ b/crates/kilt-dip-support/src/state_proofs.rs @@ -116,7 +116,7 @@ pub(super) mod relay_chain { /// Verifier of state proofs that reveal the value of a parachain head at a /// given relaychain block number. - /// The generic types indicate the following: + /// The generic types are the following: /// * `RelayChainState`: defines the relaychain runtime types relevant for /// state proof verification, and returns the relaychain runtime's storage /// key identifying a parachain with a given ID. diff --git a/crates/kilt-dip-support/src/traits.rs b/crates/kilt-dip-support/src/traits.rs index ae1c23af71..7d0967c797 100644 --- a/crates/kilt-dip-support/src/traits.rs +++ b/crates/kilt-dip-support/src/traits.rs @@ -45,7 +45,7 @@ where /// A trait for types that implement some sort of access control logic on the /// provided input `Call` type. -/// The generic types indicate the following: +/// The generic types are the following: /// * `Call`: The type of the call being checked. pub trait DipCallOriginFilter { /// The error type for cases where the checks fail. @@ -107,7 +107,7 @@ pub trait ProviderParachainStorageInfo { /// Implementation of the [`ProviderParachainStorageInfo`] trait that builds on /// the definitions of a runtime that includes the DIP provider pallet (e.g., /// KILT runtimes). -/// The generic types indicate the following: +/// The generic types are the following: /// * `T`: The runtime including the [`pallet_dip_provider::Pallet`] pallet. pub struct ProviderParachainStateInfoViaProviderPallet(PhantomData); @@ -131,8 +131,8 @@ where /// A trait that provides the consumer parachain runtime additional context to /// verify cross-chain DID signatures by subjects of the provider parachain. pub trait DidSignatureVerifierContext { - /// Max number of blocks a cross-chain DID signature is to be considered - /// fresh. + /// Max number of blocks a cross-chain DID signature can have to be + /// considered fresh. const SIGNATURE_VALIDITY: u16; /// The type of consumer parachain's block numbers. @@ -155,10 +155,10 @@ pub trait DidSignatureVerifierContext { /// Implementation of the [`DidSignatureVerifierContext`] trait that draws /// information dynamically from the consumer's runtime using its system pallet. -/// The generic types indicate the following: +/// The generic types are the following: /// * `T`: The runtime including the [`frame_system::Pallet`] pallet. -/// * `SIGNATURE_VALIDITY`: The max number of blocks DID signatures are -/// considered valid. +/// * `SIGNATURE_VALIDITY`: The max number of blocks DID signatures can have to +/// be considered valid. pub struct FrameSystemDidSignatureContext(PhantomData); impl DidSignatureVerifierContext diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index 4e32056e46..823fcd37f3 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -53,7 +53,7 @@ impl pallet_dip_consumer::Config for Runtime { type DispatchOriginCheck = EnsureSigned; type Identifier = DidIdentifier; // Local identity info contains a simple `u128` representing a nonce. This means - // that two cross-chain operations targeting the same chain and having the same + // that two cross-chain operations targeting the same chain and with the same // nonce cannot be both successfully evaluated. type LocalIdentityInfo = u128; type ProofVerifier = ProofVerifier; diff --git a/pallets/pallet-deposit-storage/src/lib.rs b/pallets/pallet-deposit-storage/src/lib.rs index 8f852bbd22..0043f9eedf 100644 --- a/pallets/pallet-deposit-storage/src/lib.rs +++ b/pallets/pallet-deposit-storage/src/lib.rs @@ -141,7 +141,7 @@ pub mod pallet { } /// Storage of all deposits. Its first key is a namespace, and the second - /// one the deposit key. Its value include the details associated to a + /// one the deposit key. Its value includes the details associated to a /// deposit instance. #[pallet::storage] #[pallet::getter(fn deposits)] diff --git a/pallets/pallet-dip-consumer/README.md b/pallets/pallet-dip-consumer/README.md index b6360066e6..e19a811d07 100644 --- a/pallets/pallet-dip-consumer/README.md +++ b/pallets/pallet-dip-consumer/README.md @@ -24,9 +24,9 @@ Other definitions for an identifier, such as a simple integer or a [Decentralize The pallet allows the consumer runtime to define some `LocalIdentityInfo` associated with each identifier, which the pallet's proof verifier can access and optionally modify upon proof verification. Any changes made to the `LocalIdentityInfo` will be persisted if the identity proof is verified correctly and the extrinsic executed successfully. -If the consumer does not need to store anything in addition to the information an identity proof conveys, they can simply use an empty tuple `()` for the local identity info. -Another example could be the use of signatures, which requires nonce to avoid replay protections. -In this case, a simple numeric type such as a `u64` or a `u128` could be used, and bumped by the proof verifies when validating each new cross-chain transaction proof. +If the consumer does not need to store anything in addition to the information an identity proof conveys, they can use an empty tuple `()` for the local identity info. +Another example could be the use of signatures, which requires a nonce to avoid replay protections. +In this case, a numeric type such as a `u64` or a `u128` could be used, and increased by the proof verifier when validating each new cross-chain transaction proof. ## The `Config` trait @@ -55,6 +55,6 @@ Because the pallet allows other `Call`s to be dispatched after an identity proof The origin is created after the identity proof has been successfully verified by the proof verifier, and it includes the identifier of the subject, the address of the tx submitter, and the result returned by the proof verifier upon successful verification. -## Calls +## Calls (bullet numbers represent each call's encoded index) -0. `pub fn dispatch_as(origin: OriginFor, identifier: T::Identifier, proof: IdentityProofOf, call: Box>) -> DispatchResult`: Try to dispatch a new local call only if it passes all the DIP requirements. Specifically, the call will be dispatched if it passes the preliminary `DipCallOriginFilter` and if the proof verifier returns a `Ok(verification_result)` value. The value is then added to the `DipOrigin` and passed down as the origin for the specified `Call`. If the whole execution terminates successfully, any changes applied to the `LocalIdentityInfo` by the proof verifier are persisted to the pallet storage. +0. `pub fn dispatch_as(origin: OriginFor, identifier: T::Identifier, proof: IdentityProofOf, call: Box>) -> DispatchResult`: Try to dispatch a new local call only if it passes all the DIP requirements. Specifically, the call will be dispatched if it passes the preliminary `DipCallOriginFilter` and if the proof verifier returns an `Ok(verification_result)` value. The value is then added to the `DipOrigin` and passed down as the origin for the specified `Call`. If the whole execution terminates successfully, any changes applied to the `LocalIdentityInfo` by the proof verifier are persisted to the pallet storage. diff --git a/pallets/pallet-dip-provider/README.md b/pallets/pallet-dip-provider/README.md index 630e6be0fa..0b384009e0 100644 --- a/pallets/pallet-dip-provider/README.md +++ b/pallets/pallet-dip-provider/README.md @@ -44,7 +44,7 @@ The `VersionedIdentityCommited` is called whenever a new commitment is stored, a Similarly, the `VersionedIdentityDeleted`, is called whenever a commitment is deleted, and contains information about the `Identifier` of the subject and the version of the commitment deleted. -## Calls +## Calls (bullet numbers represent each call's encoded index) 0. `pub fn commit_identity(origin: OriginFor, identifier: T::Identifier, version: Option ) -> DispatchResult`: Generate a new versioned commitment for the subject identified by the provided `Identifier`. If an old commitment for the same version is present, it is overridden. Hooks are called before the new commitment is stored, and optionally before the old one is replaced. 1. `pub fn delete_identity_commitment(origin: OriginFor, identifier: T::Identifier, version: Option) -> DispatchResult`: Delete an identity commitment of a specific version for a specific `Identifier`. If a commitment of the provided version does not exist for the given `Identifier`, an error is returned. Hooks are called after the commitment has been removed. diff --git a/runtimes/common/src/dip/README.md b/runtimes/common/src/dip/README.md index 4f60538e30..47664afb0d 100644 --- a/runtimes/common/src/dip/README.md +++ b/runtimes/common/src/dip/README.md @@ -7,9 +7,9 @@ Specification of the format of a DIP identity commitment and the expected format The V0 of the KILT DIP Provider specification defines the following components: * **Identity details**: What are the pieces of a KILT identity that can be used for cross-chain transactions. V0 defines them to include the following information: - * All `DidKey`s stored under the subject's DID Document. For more details about how these keys are defined, please check the [KILT DID pallet](../../../../pallets/did). - * All the `LinkableAccountId`s the DID subject has linked to the DID via the KILT linking pallet. For more details about how on-chain linking works, please check the [KILT lookup pallet](../../../../pallets/pallet-did-lookup/). - * (OPTIONAL) The web3name of the DID subject, if present. For more details about how web3names work, please check the [KILT web3name pallet](../../../../pallets/pallet-web3-names/). + * All `DidKey`s stored under the subject's DID Document. For more details about how these keys are defined, read the [KILT DID pallet](../../../../pallets/did). + * All the `LinkableAccountId`s the DID subject has linked to the DID via the KILT linking pallet. For more details about how on-chain linking works, read the [KILT lookup pallet](../../../../pallets/pallet-did-lookup/). + * (OPTIONAL) The web3name of the DID subject, if present. For more details about how web3names work, read the [KILT web3name pallet](../../../../pallets/pallet-web3-names/). * **Identity commitment**: Defines how the identity details above are aggregated into a value which will be selectively shared on a consumer chain for a cross-chain transaction. V0 defines the identity commitment as a Merkle root of all the elements above that uses the shame hashing algorithm as the runtime. Using a Merkle root allows the DID subject to generate proof that can selectively disclose different pieces of identity for different operations on different chains providing, among other things, better scalability for cases in which the linked information becomes large. The leaves encoded in the commitment can be of the following type: * DID key leaf: with leaf name being the key ID, and leaf value being the key details as defined in the `DidPublicKeyDetails` type. * Linked account leaf: with leaf name being the linked account ID, and leaf value being an empty tuple `()`. From 837bc5d9e7ee66454b622103152d762a66196ad6 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 6 Dec 2023 12:17:24 +0100 Subject: [PATCH 17/17] Revert Dockerfile changes --- Dockerfile | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index ace5ce348b..c64f906b8e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,21 +6,19 @@ FROM paritytech/ci-unified:bullseye-1.70.0 as builder WORKDIR /build ARG FEATURES=default -ARG BINARY=kilt-parachain COPY . . -RUN cargo build --locked --release --features $FEATURES -p $BINARY +RUN cargo build --locked --release --features $FEATURES # ===== SECOND STAGE ====== FROM docker.io/library/ubuntu:20.04 +LABEL description="This is the 2nd stage: a very small image where we copy the kilt-parachain binary." -ARG BINARY=kilt-parachain +ARG NODE_TYPE=kilt-parachain -LABEL description="This is the 2nd stage: a very small image where we copy the ${BINARY} binary." - -COPY --from=builder /build/target/release/$BINARY /usr/local/bin/node-executable +COPY --from=builder /build/target/release/$NODE_TYPE /usr/local/bin/node-executable RUN useradd -m -u 1000 -U -s /bin/sh -d /node node && \ mkdir -p /node/.local/share/node && \