From a895dd1135bea2f8ff3d4919c8eee8b6145881e9 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 19 Apr 2023 15:59:24 +0200 Subject: [PATCH 01/26] Pallet consumer compiling again v2 --- pallets/pallet-dip-consumer/src/lib.rs | 32 ++++++++++++------- pallets/pallet-dip-consumer/src/traits.rs | 39 +++++++++++++---------- 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index a351ae1b9a..c235e69a06 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -34,13 +34,14 @@ pub mod pallet { use frame_system::pallet_prelude::*; use sp_std::boxed::Box; - use dip_support::{latest::IdentityProofAction, VersionedIdentityProof, VersionedIdentityProofAction}; + use dip_support::{latest::IdentityProofAction, VersionedIdentityProofAction}; use crate::traits::{DipCallOriginFilter, IdentityProofVerifier}; - pub type VerificationResultOf = <::ProofVerifier as IdentityProofVerifier>::VerificationResult; - pub type VersionedIdentityProofOf = - VersionedIdentityProof<::BlindedValue, ::ProofLeaf>; + pub type VerificationResultOf = <::ProofVerifier as IdentityProofVerifier< + ::RuntimeCall, + ::Identifier, + >>::VerificationResult; const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); @@ -52,15 +53,16 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { - type BlindedValue: Parameter; type DipCallOriginFilter: DipCallOriginFilter<::RuntimeCall, Proof = VerificationResultOf>; type Identifier: Parameter + MaxEncodedLen; - type ProofLeaf: Parameter; + type Proof: Parameter; type ProofDigest: Parameter + MaxEncodedLen; type ProofVerifier: IdentityProofVerifier< - BlindedValue = Self::BlindedValue, - ProofDigest = Self::ProofDigest, - ProofLeaf = Self::ProofLeaf, + ::RuntimeCall, + Self::Identifier, + Proof = Self::Proof, + ProofEntry = Self::ProofDigest, + Submitter = ::AccountId, >; type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin>; type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -132,13 +134,19 @@ pub mod pallet { pub fn dispatch_as( origin: OriginFor, identifier: T::Identifier, - proof: VersionedIdentityProofOf, + proof: T::Proof, call: Box<::RuntimeCall>, ) -> DispatchResult { let submitter = ensure_signed(origin)?; let proof_digest = IdentityProofs::::get(&identifier).ok_or(Error::::IdentityNotFound)?; - let proof_verification_result = T::ProofVerifier::verify_proof_against_digest(proof, proof_digest) - .map_err(|_| Error::::InvalidProof)?; + let proof_verification_result = T::ProofVerifier::verify_proof_for_call_against_entry( + &*call, + &identifier, + &submitter, + &proof_digest, + proof, + ) + .map_err(|_| Error::::InvalidProof)?; // TODO: Better error handling // TODO: Avoid cloning `call` let proof_result = T::DipCallOriginFilter::check_proof(*call.clone(), proof_verification_result) diff --git a/pallets/pallet-dip-consumer/src/traits.rs b/pallets/pallet-dip-consumer/src/traits.rs index c134a038f5..016b3aef82 100644 --- a/pallets/pallet-dip-consumer/src/traits.rs +++ b/pallets/pallet-dip-consumer/src/traits.rs @@ -16,36 +16,41 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use dip_support::VersionedIdentityProof; use sp_std::marker::PhantomData; -pub trait IdentityProofVerifier { - type BlindedValue; +pub trait IdentityProofVerifier { type Error; - type ProofDigest; - type ProofLeaf; + type Proof; + type ProofEntry; + type Submitter; type VerificationResult; - fn verify_proof_against_digest( - proof: VersionedIdentityProof, - digest: Self::ProofDigest, + fn verify_proof_for_call_against_entry( + call: &Call, + subject: &Subject, + submitter: &Self::Submitter, + proof_entry: &Self::ProofEntry, + proof: Self::Proof, ) -> Result; } // Always returns success. -pub struct SuccessfulProofVerifier(PhantomData<(ProofDigest, Leaf, BlindedValue)>); -impl IdentityProofVerifier - for SuccessfulProofVerifier +pub struct SuccessfulProofVerifier(PhantomData<(Proof, ProofEntry, Submitter)>); +impl IdentityProofVerifier + for SuccessfulProofVerifier { - type BlindedValue = BlindedValue; type Error = (); - type ProofDigest = ProofDigest; - type ProofLeaf = Leaf; + type Proof = Proof; + type ProofEntry = ProofEntry; + type Submitter = Submitter; type VerificationResult = (); - fn verify_proof_against_digest( - _proof: VersionedIdentityProof, - _digest: Self::ProofDigest, + fn verify_proof_for_call_against_entry( + _call: &Call, + _subject: &Subject, + _submitter: &Self::Submitter, + _proof_entry: &Self::ProofEntry, + _proof: Self::Proof, ) -> Result { Ok(()) } From 890b2f4c25d1fadf342f6e2a5d3a4ff84f910c22 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 19 Apr 2023 16:12:50 +0200 Subject: [PATCH 02/26] Refactoring v2 completed, compiling, and XCM tests passing --- Cargo.lock | 1 + dip-template/runtimes/dip-consumer/Cargo.toml | 2 ++ dip-template/runtimes/dip-consumer/src/dip.rs | 4 +-- runtimes/common/src/dip/consumer.rs | 25 ++++++++++++------- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aaa9d25203..497ab5d25c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2278,6 +2278,7 @@ dependencies = [ "cumulus-primitives-timestamp", "cumulus-primitives-utility", "did", + "dip-support", "frame-executive", "frame-support", "frame-system", diff --git a/dip-template/runtimes/dip-consumer/Cargo.toml b/dip-template/runtimes/dip-consumer/Cargo.toml index 2073d9d275..1135dba2ad 100644 --- a/dip-template/runtimes/dip-consumer/Cargo.toml +++ b/dip-template/runtimes/dip-consumer/Cargo.toml @@ -18,6 +18,7 @@ parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} # DIP +dip-support.workspace = true did.workspace = true pallet-did-lookup.workspace = true pallet-dip-consumer.workspace = true @@ -75,6 +76,7 @@ default = [ std = [ "parity-scale-codec/std", "scale-info/std", + "dip-support/std", "did/std", "pallet-did-lookup/std", "pallet-dip-consumer/std", diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index 77fc5e79cf..c81c05b945 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -17,6 +17,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use did::DidVerificationKeyRelationship; +use dip_support::VersionedIdentityProof; use pallet_dip_consumer::traits::DipCallOriginFilter; use runtime_common::dip::{ consumer::{DidMerkleProofVerifier, VerificationResult}, @@ -27,10 +28,9 @@ use sp_std::vec::Vec; use crate::{BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; impl pallet_dip_consumer::Config for Runtime { - type BlindedValue = Vec>; type DipCallOriginFilter = DipCallFilter; type Identifier = DidIdentifier; - type ProofLeaf = ProofLeaf; + type Proof = VersionedIdentityProof>, ProofLeaf>; type ProofDigest = Hash; type ProofVerifier = DidMerkleProofVerifier; type RuntimeCall = RuntimeCall; diff --git a/runtimes/common/src/dip/consumer.rs b/runtimes/common/src/dip/consumer.rs index 8c6c492850..d1efd2a751 100644 --- a/runtimes/common/src/dip/consumer.rs +++ b/runtimes/common/src/dip/consumer.rs @@ -24,7 +24,10 @@ use parity_scale_codec::Encode; use sp_std::{collections::btree_map::BTreeMap, marker::PhantomData, vec::Vec}; use sp_trie::{verify_trie_proof, LayoutV1}; -use crate::dip::{provider, KeyDetailsKey, KeyDetailsValue, KeyReferenceKey, KeyRelationship, ProofLeaf}; +use crate::{ + dip::{provider, KeyDetailsKey, KeyDetailsValue, KeyReferenceKey, KeyRelationship, ProofLeaf}, + AccountId, +}; // TODO: Avoid repetition of the same key if it appears multiple times, e.g., by // having a vector of `KeyRelationship` instead. @@ -47,22 +50,26 @@ impl From>> for VerificationResult(PhantomData<(KeyId, BlockNumber, Hasher)>); -impl IdentityProofVerifier for DidMerkleProofVerifier +impl IdentityProofVerifier + for DidMerkleProofVerifier where KeyId: Encode + Clone + Ord, BlockNumber: Encode + Clone + Ord, Hasher: sp_core::Hasher, { - type BlindedValue = Vec; // TODO: Proper error handling type Error = (); - type ProofDigest = ::Out; - type ProofLeaf = ProofLeaf; + type Proof = VersionedIdentityProof, ProofLeaf>; + type ProofEntry = ::Out; + type Submitter = AccountId; type VerificationResult = VerificationResult; - fn verify_proof_against_digest( - proof: VersionedIdentityProof, - digest: Self::ProofDigest, + fn verify_proof_for_call_against_entry( + _call: &Call, + _subject: &Subject, + _submitter: &Self::Submitter, + proof_entry: &Self::ProofEntry, + proof: Self::Proof, ) -> Result { let proof: v1::Proof<_, _> = proof.try_into()?; // TODO: more efficient by removing cloning and/or collecting. @@ -73,7 +80,7 @@ where .iter() .map(|leaf| (leaf.encoded_key(), Some(leaf.encoded_value()))) .collect::, Option>)>>(); - verify_trie_proof::, _, _, _>(&digest, &proof.blinded, &proof_leaves).map_err(|_| ())?; + verify_trie_proof::, _, _, _>(proof_entry, &proof.blinded, &proof_leaves).map_err(|_| ())?; // At this point, we know the proof is valid. We just need to map the revealed // leaves to something the consumer can easily operate on. From 05717b7c4a25acdf17f2a4ca46ed392af1df2cff Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Thu, 20 Apr 2023 09:01:36 +0200 Subject: [PATCH 03/26] more work --- dip-template/runtimes/dip-consumer/src/dip.rs | 12 +++-- pallets/pallet-dip-consumer/src/lib.rs | 27 +++++++--- pallets/pallet-dip-consumer/src/proof.rs | 39 +++++++++++++++ runtimes/common/src/dip/consumer.rs | 50 +++++++++++++++++-- 4 files changed, 112 insertions(+), 16 deletions(-) create mode 100644 pallets/pallet-dip-consumer/src/proof.rs diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index c81c05b945..bd0c3324ec 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -16,7 +16,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::DidVerificationKeyRelationship; +use did::{DidSignature, DidVerificationKeyRelationship}; use dip_support::VersionedIdentityProof; use pallet_dip_consumer::traits::DipCallOriginFilter; use runtime_common::dip::{ @@ -27,12 +27,18 @@ use sp_std::vec::Vec; use crate::{BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; +pub struct MerkleProofAndDidSignature { + pub proof: VersionedIdentityProof, ProofLeaf>>, + pub signature: DidSignature, +} + impl pallet_dip_consumer::Config for Runtime { type DipCallOriginFilter = DipCallFilter; type Identifier = DidIdentifier; - type Proof = VersionedIdentityProof>, ProofLeaf>; + type IdentityDetails = u128; + type Proof = MerkleProofAndDidSignature; type ProofDigest = Hash; - type ProofVerifier = DidMerkleProofVerifier; + type ProofVerifier = DidMerkleProofVerifier; type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index c235e69a06..81584caec8 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -21,6 +21,7 @@ #![cfg_attr(not(feature = "std"), no_std)] mod origin; +pub mod proof; pub mod traits; pub use crate::{origin::*, pallet::*}; @@ -32,11 +33,15 @@ pub mod pallet { use cumulus_pallet_xcm::ensure_sibling_para; use frame_support::{dispatch::Dispatchable, pallet_prelude::*, Twox64Concat}; use frame_system::pallet_prelude::*; + use parity_scale_codec::MaxEncodedLen; use sp_std::boxed::Box; use dip_support::{latest::IdentityProofAction, VersionedIdentityProofAction}; - use crate::traits::{DipCallOriginFilter, IdentityProofVerifier}; + use crate::{ + proof::ProofEntry, + traits::{DipCallOriginFilter, IdentityProofVerifier}, + }; pub type VerificationResultOf = <::ProofVerifier as IdentityProofVerifier< ::RuntimeCall, @@ -48,20 +53,25 @@ pub mod pallet { // TODO: Store also additional details received by the provider. #[pallet::storage] #[pallet::getter(fn identity_proofs)] - pub(crate) type IdentityProofs = - StorageMap<_, Twox64Concat, ::Identifier, ::ProofDigest>; + pub(crate) type IdentityProofs = StorageMap< + _, + Twox64Concat, + ::Identifier, + ProofEntry<::ProofDigest, ::IdentityDetails>, + >; #[pallet::config] pub trait Config: frame_system::Config { type DipCallOriginFilter: DipCallOriginFilter<::RuntimeCall, Proof = VerificationResultOf>; type Identifier: Parameter + MaxEncodedLen; + type IdentityDetails: Parameter + MaxEncodedLen + Default; type Proof: Parameter; type ProofDigest: Parameter + MaxEncodedLen; type ProofVerifier: IdentityProofVerifier< ::RuntimeCall, Self::Identifier, Proof = Self::Proof, - ProofEntry = Self::ProofDigest, + ProofEntry = ProofEntry, Submitter = ::AccountId, >; type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin>; @@ -113,7 +123,9 @@ pub mod pallet { let event = match action { VersionedIdentityProofAction::V1(IdentityProofAction::Updated(identifier, proof, _)) => { - IdentityProofs::::mutate(&identifier, |entry| *entry = Some(proof.clone())); + IdentityProofs::::mutate(&identifier, |entry| { + *entry = Some(ProofEntry::from_digest(proof.clone())) + }); Ok::<_, Error>(Event::::IdentityInfoUpdated(identifier, proof)) } VersionedIdentityProofAction::V1(IdentityProofAction::Deleted(identifier)) => { @@ -138,12 +150,12 @@ pub mod pallet { call: Box<::RuntimeCall>, ) -> DispatchResult { let submitter = ensure_signed(origin)?; - let proof_digest = IdentityProofs::::get(&identifier).ok_or(Error::::IdentityNotFound)?; + let proof_entry = IdentityProofs::::get(&identifier).ok_or(Error::::IdentityNotFound)?; let proof_verification_result = T::ProofVerifier::verify_proof_for_call_against_entry( &*call, &identifier, &submitter, - &proof_digest, + &proof_entry, proof, ) .map_err(|_| Error::::InvalidProof)?; @@ -151,7 +163,6 @@ pub mod pallet { // TODO: Avoid cloning `call` let proof_result = T::DipCallOriginFilter::check_proof(*call.clone(), proof_verification_result) .map_err(|_| Error::::BadOrigin)?; - // TODO: Proper DID signature verification (and cross-chain replay protection) let did_origin = DipOrigin { identifier, account_address: submitter, diff --git a/pallets/pallet-dip-consumer/src/proof.rs b/pallets/pallet-dip-consumer/src/proof.rs new file mode 100644 index 0000000000..68a7d167ec --- /dev/null +++ b/pallets/pallet-dip-consumer/src/proof.rs @@ -0,0 +1,39 @@ +// 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; + +#[derive(Encode, Decode, MaxEncodedLen, Default, TypeInfo, RuntimeDebug)] +pub struct ProofEntry { + pub digest: Digest, + pub details: Details, +} + +impl ProofEntry +where + Details: Default, +{ + pub fn from_digest(digest: Digest) -> Self { + Self { + digest, + details: Details::default(), + } + } +} diff --git a/runtimes/common/src/dip/consumer.rs b/runtimes/common/src/dip/consumer.rs index d1efd2a751..e2092cc331 100644 --- a/runtimes/common/src/dip/consumer.rs +++ b/runtimes/common/src/dip/consumer.rs @@ -48,10 +48,43 @@ impl From>> for VerificationResult(PhantomData<(KeyId, BlockNumber, Hasher)>); +pub trait MerkleProof { + type BlindedValue; + type Iterator: Iterator; + type Leaf; -impl IdentityProofVerifier - for DidMerkleProofVerifier + fn blinded(&self) -> Self::BlindedValue; + fn leaves(&self) -> Self::Iterator; +} + +impl MerkleProof for VersionedIdentityProof { + type BlindedValue = Blinded; + type Iterator = Vec; + type Leaf = Leaf; + + fn blinded(&self) -> Self::BlindedValue { + match *self { + Self::V1(v1::Proof { blinded, .. }) => blinded, + // FIXME + _ => panic!("Should not reach here"), + } + } + + fn leaves(&self) -> impl Iterator { + match *self { + Self::V1(v1::Proof { revealed, .. }) => revealed, + // FIXME + _ => panic!("Should not reach here"), + } + } +} + +pub struct DidMerkleProofVerifier( + PhantomData<(KeyId, BlockNumber, Hasher, Details)>, +); + +impl IdentityProofVerifier + for DidMerkleProofVerifier where KeyId: Encode + Clone + Ord, BlockNumber: Encode + Clone + Ord, @@ -60,7 +93,7 @@ where // TODO: Proper error handling type Error = (); type Proof = VersionedIdentityProof, ProofLeaf>; - type ProofEntry = ::Out; + type ProofEntry = pallet_dip_consumer::proof::ProofEntry<::Out, Details>; type Submitter = AccountId; type VerificationResult = VerificationResult; @@ -80,7 +113,8 @@ where .iter() .map(|leaf| (leaf.encoded_key(), Some(leaf.encoded_value()))) .collect::, Option>)>>(); - verify_trie_proof::, _, _, _>(proof_entry, &proof.blinded, &proof_leaves).map_err(|_| ())?; + verify_trie_proof::, _, _, _>(&proof_entry.digest, &proof.blinded, &proof_leaves) + .map_err(|_| ())?; // At this point, we know the proof is valid. We just need to map the revealed // leaves to something the consumer can easily operate on. @@ -123,3 +157,9 @@ where Ok(keys.into()) } } + +pub struct DidSignatureVerifier; + +impl IdentityProofVerifier for DidSignatureVerifier { + type Error = (); +} From 53fb48b3e0de25b943e7d4f0c09e563b2cdd1b87 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Thu, 4 May 2023 09:43:50 +0200 Subject: [PATCH 04/26] Compiling again --- dip-template/runtimes/dip-consumer/src/dip.rs | 9 +---- runtimes/common/src/dip/consumer.rs | 37 ------------------- 2 files changed, 2 insertions(+), 44 deletions(-) diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index bd0c3324ec..9fcd4bebb0 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -16,7 +16,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::{DidSignature, DidVerificationKeyRelationship}; +use did::DidVerificationKeyRelationship; use dip_support::VersionedIdentityProof; use pallet_dip_consumer::traits::DipCallOriginFilter; use runtime_common::dip::{ @@ -27,16 +27,11 @@ use sp_std::vec::Vec; use crate::{BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; -pub struct MerkleProofAndDidSignature { - pub proof: VersionedIdentityProof, ProofLeaf>>, - pub signature: DidSignature, -} - impl pallet_dip_consumer::Config for Runtime { type DipCallOriginFilter = DipCallFilter; type Identifier = DidIdentifier; type IdentityDetails = u128; - type Proof = MerkleProofAndDidSignature; + type Proof = VersionedIdentityProof>, ProofLeaf>; type ProofDigest = Hash; type ProofVerifier = DidMerkleProofVerifier; type RuntimeCall = RuntimeCall; diff --git a/runtimes/common/src/dip/consumer.rs b/runtimes/common/src/dip/consumer.rs index e2092cc331..30f115bade 100644 --- a/runtimes/common/src/dip/consumer.rs +++ b/runtimes/common/src/dip/consumer.rs @@ -48,37 +48,6 @@ impl From>> for VerificationResult; - type Leaf; - - fn blinded(&self) -> Self::BlindedValue; - fn leaves(&self) -> Self::Iterator; -} - -impl MerkleProof for VersionedIdentityProof { - type BlindedValue = Blinded; - type Iterator = Vec; - type Leaf = Leaf; - - fn blinded(&self) -> Self::BlindedValue { - match *self { - Self::V1(v1::Proof { blinded, .. }) => blinded, - // FIXME - _ => panic!("Should not reach here"), - } - } - - fn leaves(&self) -> impl Iterator { - match *self { - Self::V1(v1::Proof { revealed, .. }) => revealed, - // FIXME - _ => panic!("Should not reach here"), - } - } -} - pub struct DidMerkleProofVerifier( PhantomData<(KeyId, BlockNumber, Hasher, Details)>, ); @@ -157,9 +126,3 @@ where Ok(keys.into()) } } - -pub struct DidSignatureVerifier; - -impl IdentityProofVerifier for DidSignatureVerifier { - type Error = (); -} From 4c672cf32ce6ed6afe5335af0c09e37764a2786e Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Thu, 4 May 2023 15:58:39 +0200 Subject: [PATCH 05/26] Project compiling. Need to add DID signature verification to the runtime --- crates/dip/src/lib.rs | 10 +- crates/dip/src/v1.rs | 2 +- dip-template/runtimes/dip-consumer/src/dip.rs | 4 +- dip-template/runtimes/xcm-tests/src/tests.rs | 4 +- pallets/pallet-dip-consumer/src/proof.rs | 9 ++ runtimes/common/src/dip/consumer.rs | 137 ++++++++++++++++-- runtimes/common/src/dip/provider.rs | 5 +- runtimes/common/src/dip/tests.rs | 4 +- 8 files changed, 149 insertions(+), 26 deletions(-) diff --git a/crates/dip/src/lib.rs b/crates/dip/src/lib.rs index a795d7c855..c0ff96f0eb 100644 --- a/crates/dip/src/lib.rs +++ b/crates/dip/src/lib.rs @@ -49,22 +49,22 @@ impl From { #[codec(index = 1)] - V1(v1::Proof), + V1(v1::MerkleProof), } -impl From> for VersionedIdentityProof { - fn from(value: v1::Proof) -> Self { +impl From> for VersionedIdentityProof { + fn from(value: v1::MerkleProof) -> Self { Self::V1(value) } } -impl TryFrom> for v1::Proof { +impl TryFrom> for v1::MerkleProof { // Proper error handling type Error = (); fn try_from(value: VersionedIdentityProof) -> Result { #[allow(irrefutable_let_patterns)] - if let VersionedIdentityProof::V1(v1::Proof { blinded, revealed }) = value { + if let VersionedIdentityProof::V1(v1::MerkleProof { blinded, revealed }) = value { Ok(Self { blinded, revealed }) } else { Err(()) diff --git a/crates/dip/src/v1.rs b/crates/dip/src/v1.rs index bdbda97bf6..f4ff7fab51 100644 --- a/crates/dip/src/v1.rs +++ b/crates/dip/src/v1.rs @@ -28,7 +28,7 @@ pub enum IdentityProofAction { } #[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo, Default)] -pub struct Proof { +pub struct MerkleProof { pub blinded: BlindedValue, // TODO: Probably replace with a different data structure for better lookup capabilities pub revealed: Vec, diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index 9fcd4bebb0..570444f846 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -25,7 +25,7 @@ use runtime_common::dip::{ }; use sp_std::vec::Vec; -use crate::{BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; +use crate::{AccountId, BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; impl pallet_dip_consumer::Config for Runtime { type DipCallOriginFilter = DipCallFilter; @@ -33,7 +33,7 @@ impl pallet_dip_consumer::Config for Runtime { type IdentityDetails = u128; type Proof = VersionedIdentityProof>, ProofLeaf>; type ProofDigest = Hash; - type ProofVerifier = DidMerkleProofVerifier; + type ProofVerifier = DidMerkleProofVerifier; type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index 9c04ad8abb..c2b07da8b3 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -19,7 +19,7 @@ use super::*; use did::Did; -use dip_support::latest::Proof; +use dip_support::latest::MerkleProof; use frame_support::{assert_ok, weights::Weight}; use frame_system::RawOrigin; use pallet_did_lookup::linkable_account::LinkableAccountId; @@ -85,7 +85,7 @@ fn commit_identity() { assert_ok!(DipConsumer::dispatch_as( RawOrigin::Signed(para::consumer::DISPATCHER_ACCOUNT).into(), did.clone(), - Proof { + MerkleProof { blinded: proof.blinded, revealed: proof.revealed, } diff --git a/pallets/pallet-dip-consumer/src/proof.rs b/pallets/pallet-dip-consumer/src/proof.rs index 68a7d167ec..3c9e22700c 100644 --- a/pallets/pallet-dip-consumer/src/proof.rs +++ b/pallets/pallet-dip-consumer/src/proof.rs @@ -26,6 +26,15 @@ pub struct ProofEntry { pub details: Details, } +impl ProofEntry { + pub fn digest(&self) -> &Digest { + &self.digest + } + pub fn details(&self) -> &Details { + &self.details + } +} + impl ProofEntry where Details: Default, diff --git a/runtimes/common/src/dip/consumer.rs b/runtimes/common/src/dip/consumer.rs index 30f115bade..a26651dce4 100644 --- a/runtimes/common/src/dip/consumer.rs +++ b/runtimes/common/src/dip/consumer.rs @@ -16,7 +16,10 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::did_details::DidPublicKeyDetails; +use did::{ + did_details::{DidPublicKey, DidPublicKeyDetails}, + DidSignature, +}; use dip_support::{v1, VersionedIdentityProof}; use frame_support::RuntimeDebug; use pallet_dip_consumer::traits::IdentityProofVerifier; @@ -24,14 +27,11 @@ use parity_scale_codec::Encode; use sp_std::{collections::btree_map::BTreeMap, marker::PhantomData, vec::Vec}; use sp_trie::{verify_trie_proof, LayoutV1}; -use crate::{ - dip::{provider, KeyDetailsKey, KeyDetailsValue, KeyReferenceKey, KeyRelationship, ProofLeaf}, - AccountId, -}; +use crate::dip::{provider, KeyDetailsKey, KeyDetailsValue, KeyReferenceKey, KeyRelationship, ProofLeaf}; // TODO: Avoid repetition of the same key if it appears multiple times, e.g., by // having a vector of `KeyRelationship` instead. -#[derive(RuntimeDebug, PartialEq, Eq)] +#[derive(Clone, RuntimeDebug, PartialEq, Eq)] pub struct ProofEntry { pub key: DidPublicKeyDetails, pub relationship: KeyRelationship, @@ -39,7 +39,7 @@ pub struct ProofEntry { // Contains the list of revealed public keys after a given merkle proof has been // correctly verified. -#[derive(RuntimeDebug, PartialEq, Eq)] +#[derive(Clone, RuntimeDebug, PartialEq, Eq)] pub struct VerificationResult(pub Vec>); impl From>> for VerificationResult { @@ -48,12 +48,12 @@ impl From>> for VerificationResult( - PhantomData<(KeyId, BlockNumber, Hasher, Details)>, +pub struct DidMerkleProofVerifier( + PhantomData<(KeyId, BlockNumber, Hasher, Details, AccountId)>, ); -impl IdentityProofVerifier - for DidMerkleProofVerifier +impl IdentityProofVerifier + for DidMerkleProofVerifier where KeyId: Encode + Clone + Ord, BlockNumber: Encode + Clone + Ord, @@ -73,7 +73,7 @@ where proof_entry: &Self::ProofEntry, proof: Self::Proof, ) -> Result { - let proof: v1::Proof<_, _> = proof.try_into()?; + let proof: v1::MerkleProof<_, _> = proof.try_into()?; // TODO: more efficient by removing cloning and/or collecting. // Did not find another way of mapping a Vec<(Vec, Vec)> to a // Vec<(Vec, Option>)>. @@ -126,3 +126,116 @@ where Ok(keys.into()) } } + +// #[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)] +// pub struct DidProof { +// pub payload: Payload, +// pub signature: DidSignature, +// } + +// pub type DidSignaturePayload() + +pub struct DidSignatureVerifier( + PhantomData<(Hasher, Details, AccountId, BlockNumber)>, +); + +impl IdentityProofVerifier + for DidSignatureVerifier +where + Hasher: sp_core::Hasher, + Call: Encode, + Details: Encode, + AccountId: Encode, +{ + // TODO: Error handling + type Error = (); + type Proof = (Vec>, DidSignature); + type ProofEntry = pallet_dip_consumer::proof::ProofEntry<::Out, Details>; + type Submitter = AccountId; + type VerificationResult = (); + + fn verify_proof_for_call_against_entry( + call: &Call, + _subject: &Subject, + submitter: &Self::Submitter, + proof_entry: &Self::ProofEntry, + proof: Self::Proof, + ) -> Result { + let encoded_payload = (call, proof_entry.details(), submitter).encode(); + let mut proof_verification_keys = proof.0.iter().filter_map( + |ProofEntry { + key: DidPublicKeyDetails { key, .. }, + .. + }| { + if let DidPublicKey::PublicVerificationKey(k) = key { + Some(k) + } else { + None + } + }, + ); + let is_signature_valid_for_revealed_keys = proof_verification_keys + .any(|verification_key| verification_key.verify_signature(&encoded_payload, &proof.1).is_ok()); + if is_signature_valid_for_revealed_keys { + Ok(()) + } else { + Err(()) + } + } +} + +pub struct KiltDipProof { + identity_proof: VersionedIdentityProof, ProofLeaf>, + did_signature: DidSignature, +} + +pub struct KiltDipProofVerifier( + PhantomData<(KeyId, BlockNumber, Hasher, Details, AccountId)>, +); + +impl IdentityProofVerifier + for KiltDipProofVerifier +where + Hasher: sp_core::Hasher, + Call: Encode, + Details: Encode, + AccountId: Encode, + KeyId: Encode + Clone + Ord, + BlockNumber: Encode + Clone + Ord, +{ + // TODO: Error handling + type Error = (); + type Proof = KiltDipProof; + type ProofEntry = pallet_dip_consumer::proof::ProofEntry<::Out, Details>; + type Submitter = AccountId; + type VerificationResult = + as IdentityProofVerifier< + Call, + Subject, + >>::VerificationResult; + + fn verify_proof_for_call_against_entry( + call: &Call, + subject: &Subject, + submitter: &Self::Submitter, + proof_entry: &Self::ProofEntry, + proof: Self::Proof, + ) -> Result { + let merkle_proof_verification = + DidMerkleProofVerifier::::verify_proof_for_call_against_entry( + call, + subject, + submitter, + proof_entry, + proof.identity_proof, + )?; + DidSignatureVerifier::::verify_proof_for_call_against_entry( + call, + subject, + submitter, + proof_entry, + (merkle_proof_verification.0.clone(), proof.did_signature), + )?; + Ok(merkle_proof_verification) + } +} diff --git a/runtimes/common/src/dip/provider.rs b/runtimes/common/src/dip/provider.rs index 0f555a199a..6edf18135a 100644 --- a/runtimes/common/src/dip/provider.rs +++ b/runtimes/common/src/dip/provider.rs @@ -17,7 +17,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use did::{did_details::DidDetails, DidVerificationKeyRelationship, KeyIdOf}; -use dip_support::latest::Proof; +use dip_support::latest::MerkleProof; use frame_support::RuntimeDebug; use pallet_dip_provider::traits::{IdentityProofGenerator, IdentityProvider}; use parity_scale_codec::{Decode, Encode}; @@ -29,7 +29,8 @@ use crate::dip::{KeyDetailsKey, KeyDetailsValue, KeyReferenceKey, KeyReferenceVa pub type BlindedValue = Vec; -pub type DidMerkleProof = Proof, ProofLeaf, ::BlockNumber>>; +pub type DidMerkleProof = + MerkleProof, ProofLeaf, ::BlockNumber>>; #[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] pub struct CompleteMerkleProof { diff --git a/runtimes/common/src/dip/tests.rs b/runtimes/common/src/dip/tests.rs index b1ce9f9430..9834ad7a63 100644 --- a/runtimes/common/src/dip/tests.rs +++ b/runtimes/common/src/dip/tests.rs @@ -20,7 +20,7 @@ use did::{ did_details::{DidCreationDetails, DidEncryptionKey}, DidVerificationKeyRelationship, KeyIdOf, }; -use dip_support::latest::Proof; +use dip_support::latest::MerkleProof; use frame_support::{ assert_err, assert_ok, construct_runtime, parameter_types, traits::Everything, weights::constants::RocksDbWeight, }; @@ -302,7 +302,7 @@ fn complete_did_merkle_proof() { // Fail to verify the generated merkle proof assert_err!( DidMerkleProofVerifier::, BlockNumber, Hashing>::verify_proof_against_digest( - Proof { + MerkleProof { blinded: proof.blinded, revealed: reference_only_authentication_leaf } From f186da11a2c6ba9658d35640decb14d43a5a4e31 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Mon, 8 May 2023 08:26:37 +0200 Subject: [PATCH 06/26] Pallet consumer new logic --- pallets/pallet-dip-consumer/src/lib.rs | 26 +++++++++++------------ pallets/pallet-dip-consumer/src/traits.rs | 4 ++-- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index 81584caec8..6fcf708f4f 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -31,17 +31,14 @@ pub mod pallet { use super::*; use cumulus_pallet_xcm::ensure_sibling_para; - use frame_support::{dispatch::Dispatchable, pallet_prelude::*, Twox64Concat}; + use frame_support::{dispatch::Dispatchable, pallet_prelude::*, traits::Contains, Twox64Concat}; use frame_system::pallet_prelude::*; use parity_scale_codec::MaxEncodedLen; use sp_std::boxed::Box; use dip_support::{latest::IdentityProofAction, VersionedIdentityProofAction}; - use crate::{ - proof::ProofEntry, - traits::{DipCallOriginFilter, IdentityProofVerifier}, - }; + use crate::{proof::ProofEntry, traits::IdentityProofVerifier}; pub type VerificationResultOf = <::ProofVerifier as IdentityProofVerifier< ::RuntimeCall, @@ -62,7 +59,7 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { - type DipCallOriginFilter: DipCallOriginFilter<::RuntimeCall, Proof = VerificationResultOf>; + type DipCallOriginFilter: Contains<::RuntimeCall>; type Identifier: Parameter + MaxEncodedLen; type IdentityDetails: Parameter + MaxEncodedLen + Default; type Proof: Parameter; @@ -107,7 +104,10 @@ pub mod pallet { pub type Origin = DipOrigin< ::Identifier, ::AccountId, - <::DipCallOriginFilter as DipCallOriginFilter<::RuntimeCall>>::Success, + <::ProofVerifier as IdentityProofVerifier< + ::RuntimeCall, + ::Identifier, + >>::VerificationResult, >; // TODO: Benchmarking @@ -150,23 +150,21 @@ pub mod pallet { call: Box<::RuntimeCall>, ) -> DispatchResult { let submitter = ensure_signed(origin)?; - let proof_entry = IdentityProofs::::get(&identifier).ok_or(Error::::IdentityNotFound)?; + // TODO: Proper error handling + ensure!(T::DipCallOriginFilter::contains(&*call), Error::::Dispatch); + let mut proof_entry = IdentityProofs::::get(&identifier).ok_or(Error::::IdentityNotFound)?; let proof_verification_result = T::ProofVerifier::verify_proof_for_call_against_entry( &*call, &identifier, &submitter, - &proof_entry, + &mut proof_entry, proof, ) .map_err(|_| Error::::InvalidProof)?; - // TODO: Better error handling - // TODO: Avoid cloning `call` - let proof_result = T::DipCallOriginFilter::check_proof(*call.clone(), proof_verification_result) - .map_err(|_| Error::::BadOrigin)?; let did_origin = DipOrigin { identifier, account_address: submitter, - details: proof_result, + details: proof_verification_result, }; // TODO: Use dispatch info for weight calculation let _ = call.dispatch(did_origin.into()).map_err(|_| Error::::Dispatch)?; diff --git a/pallets/pallet-dip-consumer/src/traits.rs b/pallets/pallet-dip-consumer/src/traits.rs index 016b3aef82..f1aff6f4fe 100644 --- a/pallets/pallet-dip-consumer/src/traits.rs +++ b/pallets/pallet-dip-consumer/src/traits.rs @@ -29,7 +29,7 @@ pub trait IdentityProofVerifier { call: &Call, subject: &Subject, submitter: &Self::Submitter, - proof_entry: &Self::ProofEntry, + proof_entry: &mut Self::ProofEntry, proof: Self::Proof, ) -> Result; } @@ -49,7 +49,7 @@ impl IdentityProofVerifier Result { Ok(()) From 3645fab877eccd23eddd6b6767adc9ef02be4091 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Mon, 8 May 2023 08:42:50 +0200 Subject: [PATCH 07/26] More pallet consumer changes --- pallets/pallet-dip-consumer/src/lib.rs | 4 +++- pallets/pallet-dip-consumer/src/traits.rs | 8 -------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index 6fcf708f4f..c2f3f1ca80 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -92,7 +92,6 @@ pub mod pallet { #[pallet::error] pub enum Error { - BadOrigin, Dispatch, IdentityNotFound, InvalidProof, @@ -161,6 +160,9 @@ pub mod pallet { proof, ) .map_err(|_| Error::::InvalidProof)?; + // Update the identity info after it has optionally been updated by the + // `ProofVerifier`. + IdentityProofs::::mutate(&identifier, |entry| *entry = Some(proof_entry)); let did_origin = DipOrigin { identifier, account_address: submitter, diff --git a/pallets/pallet-dip-consumer/src/traits.rs b/pallets/pallet-dip-consumer/src/traits.rs index f1aff6f4fe..896395350d 100644 --- a/pallets/pallet-dip-consumer/src/traits.rs +++ b/pallets/pallet-dip-consumer/src/traits.rs @@ -55,11 +55,3 @@ impl IdentityProofVerifier { - type Error; - type Proof; - type Success; - - fn check_proof(call: Call, proof: Self::Proof) -> Result; -} From 44ced39a74e1699c3978bb8baba2b9d6185e1772 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Mon, 8 May 2023 15:21:34 +0200 Subject: [PATCH 08/26] Compiling! --- Cargo.toml | 2 +- crates/{dip => dip-support}/Cargo.toml | 6 +- crates/{dip => dip-support}/src/lib.rs | 0 crates/{dip => dip-support}/src/v1.rs | 0 dip-template/runtimes/dip-consumer/src/dip.rs | 67 ++++-- dip-template/runtimes/dip-consumer/src/lib.rs | 5 +- pallets/pallet-dip-consumer/src/lib.rs | 2 +- pallets/pallet-dip-consumer/src/traits.rs | 4 +- runtimes/common/src/dip/consumer.rs | 220 +++++++++++------- runtimes/common/src/dip/mod.rs | 17 +- runtimes/common/src/lib.rs | 6 +- runtimes/peregrine/src/lib.rs | 4 +- runtimes/spiritnet/src/lib.rs | 4 +- 13 files changed, 216 insertions(+), 121 deletions(-) rename crates/{dip => dip-support}/Cargo.toml (94%) rename crates/{dip => dip-support}/src/lib.rs (100%) rename crates/{dip => dip-support}/src/v1.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 221c4cac81..d912c160e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,7 +60,7 @@ parachain-staking = {path = "pallets/parachain-staking", default-features = fals public-credentials = {path = "pallets/public-credentials", default-features = false} # Internal support (with default disabled) -dip-support = {path = "crates/dip", default-features = false} +dip-support = {path = "crates/dip-support", default-features = false} kilt-asset-dids = {path = "crates/assets", default-features = false} kilt-support = {path = "support", default-features = false} runtime-common = {path = "runtimes/common", default-features = false} diff --git a/crates/dip/Cargo.toml b/crates/dip-support/Cargo.toml similarity index 94% rename from crates/dip/Cargo.toml rename to crates/dip-support/Cargo.toml index 0b0a7af1e1..d09a0e5413 100644 --- a/crates/dip/Cargo.toml +++ b/crates/dip-support/Cargo.toml @@ -14,16 +14,16 @@ version.workspace = true # Parity dependencies parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} -sp-std.workspace = true # Substrate dependencies frame-support.workspace = true +sp-std.workspace = true [features] default = ["std"] std = [ "parity-scale-codec/std", "scale-info/std", - "sp-std/std", - "frame-support/std" + "frame-support/std", + "sp-std/std" ] diff --git a/crates/dip/src/lib.rs b/crates/dip-support/src/lib.rs similarity index 100% rename from crates/dip/src/lib.rs rename to crates/dip-support/src/lib.rs diff --git a/crates/dip/src/v1.rs b/crates/dip-support/src/v1.rs similarity index 100% rename from crates/dip/src/v1.rs rename to crates/dip-support/src/v1.rs diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index 570444f846..322d77d066 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -16,35 +16,50 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::DidVerificationKeyRelationship; +use did::{did_details::DidVerificationKey, DidVerificationKeyRelationship}; use dip_support::VersionedIdentityProof; -use pallet_dip_consumer::traits::DipCallOriginFilter; +use frame_support::traits::Contains; use runtime_common::dip::{ - consumer::{DidMerkleProofVerifier, VerificationResult}, + consumer::{DidDipOriginFilter, DidMerkleProofVerifier}, ProofLeaf, }; use sp_std::vec::Vec; -use crate::{AccountId, BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; +use crate::{BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; impl pallet_dip_consumer::Config for Runtime { - type DipCallOriginFilter = DipCallFilter; + type DipCallOriginFilter = PreliminaryDipOriginFilter; type Identifier = DidIdentifier; - type IdentityDetails = u128; + type IdentityDetails = (); + // FIXME: Change with a proper type once everything else works. type Proof = VersionedIdentityProof>, ProofLeaf>; type ProofDigest = Hash; - type ProofVerifier = DidMerkleProofVerifier; + type ProofVerifier = DidMerkleProofVerifier; type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; } +pub struct PreliminaryDipOriginFilter; + +impl Contains for PreliminaryDipOriginFilter { + fn contains(t: &RuntimeCall) -> bool { + matches!( + t, + RuntimeCall::DidLookup { .. } + | RuntimeCall::Utility(pallet_utility::Call::batch { .. }) + | RuntimeCall::Utility(pallet_utility::Call::batch_all { .. }) + | RuntimeCall::Utility(pallet_utility::Call::force_batch { .. }) + ) + } +} + fn derive_verification_key_relationship(call: &RuntimeCall) -> Option { match call { RuntimeCall::DidLookup { .. } => Some(DidVerificationKeyRelationship::Authentication), - RuntimeCall::Utility(pallet_utility::Call::batch { calls }) => single_key_relationship(calls).ok(), - RuntimeCall::Utility(pallet_utility::Call::batch_all { calls }) => single_key_relationship(calls).ok(), - RuntimeCall::Utility(pallet_utility::Call::force_batch { calls }) => single_key_relationship(calls).ok(), + RuntimeCall::Utility(pallet_utility::Call::batch { calls }) => single_key_relationship(calls.iter()).ok(), + RuntimeCall::Utility(pallet_utility::Call::batch_all { calls }) => single_key_relationship(calls.iter()).ok(), + RuntimeCall::Utility(pallet_utility::Call::force_batch { calls }) => single_key_relationship(calls.iter()).ok(), _ => None, } } @@ -52,11 +67,15 @@ fn derive_verification_key_relationship(call: &RuntimeCall) -> Option Result { - let first_call_relationship = calls.get(0).and_then(derive_verification_key_relationship).ok_or(())?; +fn single_key_relationship<'a>( + calls: impl Iterator, +) -> Result { + let mut calls = calls.peekable(); + let first_call_relationship = calls + .peek() + .and_then(|k| derive_verification_key_relationship(k)) + .ok_or(())?; calls - .iter() - .skip(1) .map(derive_verification_key_relationship) .try_fold(first_call_relationship, |acc, next| { if next == Some(acc) { @@ -69,15 +88,15 @@ fn single_key_relationship(calls: &[RuntimeCall]) -> Result for DipCallFilter { +impl DidDipOriginFilter for DipCallFilter { type Error = (); - type Proof = VerificationResult; + type OriginInfo = (DidVerificationKey, DidVerificationKeyRelationship); type Success = (); // Accepts only a DipOrigin for the DidLookup pallet calls. - fn check_proof(call: RuntimeCall, proof: Self::Proof) -> Result { - let key_relationship = single_key_relationship(&[call])?; - if proof.0.iter().any(|l| l.relationship == key_relationship.into()) { + fn check_call_origin_info(call: &RuntimeCall, info: &Self::OriginInfo) -> Result { + let key_relationship = single_key_relationship([call].into_iter())?; + if info.1 == key_relationship { Ok(()) } else { Err(()) @@ -96,21 +115,21 @@ mod dip_call_origin_filter_tests { // Can call DidLookup functions with an authentication key let did_lookup_call = RuntimeCall::DidLookup(pallet_did_lookup::Call::associate_sender {}); assert_eq!( - single_key_relationship(&[did_lookup_call]), + single_key_relationship(&[&did_lookup_call]), Ok(DidVerificationKeyRelationship::Authentication) ); // Can't call System functions with a DID key (hence a DIP origin) let system_call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); - assert_err!(single_key_relationship(&[system_call]), ()); + assert_err!(single_key_relationship(&[&system_call]), ()); // Can't call empty batch with a DID key let empty_batch_call = RuntimeCall::Utility(pallet_utility::Call::batch_all { calls: vec![] }); - assert_err!(single_key_relationship(&[empty_batch_call]), ()); + assert_err!(single_key_relationship(&[&empty_batch_call]), ()); // Can call batch with a DipLookup with an authentication key let did_lookup_batch_call = RuntimeCall::Utility(pallet_utility::Call::batch_all { calls: vec![pallet_did_lookup::Call::associate_sender {}.into()], }); assert_eq!( - single_key_relationship(&[did_lookup_batch_call]), + single_key_relationship(&[&did_lookup_batch_call]), Ok(DidVerificationKeyRelationship::Authentication) ); // Can't call a batch with different required keys @@ -122,6 +141,6 @@ mod dip_call_origin_filter_tests { frame_system::Call::remark { remark: vec![] }.into(), ], }); - assert_err!(single_key_relationship(&[did_lookup_batch_call]), ()); + assert_err!(single_key_relationship(&[&did_lookup_batch_call]), ()); } } diff --git a/dip-template/runtimes/dip-consumer/src/lib.rs b/dip-template/runtimes/dip-consumer/src/lib.rs index 955a43885f..19b86e5c14 100644 --- a/dip-template/runtimes/dip-consumer/src/lib.rs +++ b/dip-template/runtimes/dip-consumer/src/lib.rs @@ -22,6 +22,7 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +use runtime_common::dip::consumer::VerificationResult; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::{MultiAddress, Perbill, Permill}; @@ -366,8 +367,8 @@ impl pallet_did_lookup::Config for Runtime { type Currency = Balances; type Deposit = ConstU128; type DidIdentifier = DidIdentifier; - type EnsureOrigin = EnsureDipOrigin; - type OriginSuccess = DipOrigin; + type EnsureOrigin = EnsureDipOrigin>; + type OriginSuccess = DipOrigin>; type RuntimeEvent = RuntimeEvent; type WeightInfo = (); } diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index c2f3f1ca80..36e5661d2d 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -157,7 +157,7 @@ pub mod pallet { &identifier, &submitter, &mut proof_entry, - proof, + &proof, ) .map_err(|_| Error::::InvalidProof)?; // Update the identity info after it has optionally been updated by the diff --git a/pallets/pallet-dip-consumer/src/traits.rs b/pallets/pallet-dip-consumer/src/traits.rs index 896395350d..f437672b1a 100644 --- a/pallets/pallet-dip-consumer/src/traits.rs +++ b/pallets/pallet-dip-consumer/src/traits.rs @@ -30,7 +30,7 @@ pub trait IdentityProofVerifier { subject: &Subject, submitter: &Self::Submitter, proof_entry: &mut Self::ProofEntry, - proof: Self::Proof, + proof: &Self::Proof, ) -> Result; } @@ -50,7 +50,7 @@ impl IdentityProofVerifier Result { Ok(()) } diff --git a/runtimes/common/src/dip/consumer.rs b/runtimes/common/src/dip/consumer.rs index a26651dce4..be91d87126 100644 --- a/runtimes/common/src/dip/consumer.rs +++ b/runtimes/common/src/dip/consumer.rs @@ -16,22 +16,30 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +use core::fmt::Debug; + use did::{ - did_details::{DidPublicKey, DidPublicKeyDetails}, - DidSignature, + did_details::{DidPublicKey, DidPublicKeyDetails, DidVerificationKey}, + DidSignature, DidVerificationKeyRelationship, }; use dip_support::{v1, VersionedIdentityProof}; -use frame_support::RuntimeDebug; +use frame_support::{BoundedVec, RuntimeDebug}; use pallet_dip_consumer::traits::IdentityProofVerifier; -use parity_scale_codec::Encode; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_core::ConstU32; +use sp_runtime::traits::{CheckedAdd, One, Zero}; use sp_std::{collections::btree_map::BTreeMap, marker::PhantomData, vec::Vec}; use sp_trie::{verify_trie_proof, LayoutV1}; -use crate::dip::{provider, KeyDetailsKey, KeyDetailsValue, KeyReferenceKey, KeyRelationship, ProofLeaf}; +use crate::{ + dip::{provider, KeyDetailsKey, KeyDetailsValue, KeyReferenceKey, KeyRelationship, ProofLeaf}, + AccountId, Hash, +}; // TODO: Avoid repetition of the same key if it appears multiple times, e.g., by // having a vector of `KeyRelationship` instead. -#[derive(Clone, RuntimeDebug, PartialEq, Eq)] +#[derive(Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen, Encode, Decode)] pub struct ProofEntry { pub key: DidPublicKeyDetails, pub relationship: KeyRelationship, @@ -39,30 +47,31 @@ pub struct ProofEntry { // Contains the list of revealed public keys after a given merkle proof has been // correctly verified. -#[derive(Clone, RuntimeDebug, PartialEq, Eq)] -pub struct VerificationResult(pub Vec>); +#[derive(Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen, Encode, Decode)] +pub struct VerificationResult(pub BoundedVec, ConstU32<10>>); -impl From>> for VerificationResult { +impl From>> for VerificationResult +where + BlockNumber: Debug, +{ fn from(value: Vec>) -> Self { - Self(value) + Self(value.try_into().expect("Failed to put Vec into BoundedVec")) } } -pub struct DidMerkleProofVerifier( - PhantomData<(KeyId, BlockNumber, Hasher, Details, AccountId)>, -); +pub struct DidMerkleProofVerifier(PhantomData<(Hasher, BlockNumber, Details)>); -impl IdentityProofVerifier - for DidMerkleProofVerifier +impl IdentityProofVerifier + for DidMerkleProofVerifier where - KeyId: Encode + Clone + Ord, - BlockNumber: Encode + Clone + Ord, + BlockNumber: Encode + Clone + Debug, Hasher: sp_core::Hasher, + Hasher::Out: From, { // TODO: Proper error handling type Error = (); - type Proof = VersionedIdentityProof, ProofLeaf>; - type ProofEntry = pallet_dip_consumer::proof::ProofEntry<::Out, Details>; + type Proof = VersionedIdentityProof, ProofLeaf>; + type ProofEntry = pallet_dip_consumer::proof::ProofEntry; type Submitter = AccountId; type VerificationResult = VerificationResult; @@ -70,10 +79,10 @@ where _call: &Call, _subject: &Subject, _submitter: &Self::Submitter, - proof_entry: &Self::ProofEntry, - proof: Self::Proof, + proof_entry: &mut Self::ProofEntry, + proof: &Self::Proof, ) -> Result { - let proof: v1::MerkleProof<_, _> = proof.try_into()?; + let proof: v1::MerkleProof<_, _> = proof.clone().try_into()?; // TODO: more efficient by removing cloning and/or collecting. // Did not find another way of mapping a Vec<(Vec, Vec)> to a // Vec<(Vec, Option>)>. @@ -82,7 +91,7 @@ where .iter() .map(|leaf| (leaf.encoded_key(), Some(leaf.encoded_value()))) .collect::, Option>)>>(); - verify_trie_proof::, _, _, _>(&proof_entry.digest, &proof.blinded, &proof_leaves) + verify_trie_proof::, _, _, _>(&proof_entry.digest.into(), &proof.blinded, &proof_leaves) .map_err(|_| ())?; // At this point, we know the proof is valid. We just need to map the revealed @@ -90,7 +99,7 @@ where // Create a map of the revealed public keys //TODO: Avoid cloning, and use a map of references for the lookup - let public_keys: BTreeMap> = proof + let public_keys: BTreeMap> = proof .revealed .clone() .into_iter() @@ -127,115 +136,166 @@ where } } -// #[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)] -// pub struct DidProof { -// pub payload: Payload, -// pub signature: DidSignature, -// } +pub trait Bump { + fn bump(&mut self); +} -// pub type DidSignaturePayload() +impl Bump for T +where + T: CheckedAdd + Zero + One, +{ + // FIXME: Better implementation? + fn bump(&mut self) { + if let Some(new) = self.checked_add(&Self::one()) { + *self = new; + } else { + *self = Self::zero(); + } + } +} -pub struct DidSignatureVerifier( - PhantomData<(Hasher, Details, AccountId, BlockNumber)>, -); +// Verifies a DID signature over the call details, which is the encoded tuple of +// (call, proof_entry.details(), submitter address). +pub struct DidSignatureVerifier(PhantomData<(BlockNumber, Details)>); -impl IdentityProofVerifier - for DidSignatureVerifier +impl IdentityProofVerifier + for DidSignatureVerifier where - Hasher: sp_core::Hasher, + BlockNumber: Encode, Call: Encode, - Details: Encode, - AccountId: Encode, + Details: Bump + Encode, { // TODO: Error handling type Error = (); type Proof = (Vec>, DidSignature); - type ProofEntry = pallet_dip_consumer::proof::ProofEntry<::Out, Details>; + type ProofEntry = pallet_dip_consumer::proof::ProofEntry; type Submitter = AccountId; - type VerificationResult = (); + type VerificationResult = (DidVerificationKey, DidVerificationKeyRelationship); fn verify_proof_for_call_against_entry( call: &Call, _subject: &Subject, submitter: &Self::Submitter, - proof_entry: &Self::ProofEntry, - proof: Self::Proof, + proof_entry: &mut Self::ProofEntry, + proof: &Self::Proof, ) -> Result { let encoded_payload = (call, proof_entry.details(), submitter).encode(); let mut proof_verification_keys = proof.0.iter().filter_map( |ProofEntry { key: DidPublicKeyDetails { key, .. }, - .. + relationship, }| { if let DidPublicKey::PublicVerificationKey(k) = key { - Some(k) + Some(( + k, + DidVerificationKeyRelationship::try_from(*relationship).expect("Should never fail."), + )) } else { None } }, ); - let is_signature_valid_for_revealed_keys = proof_verification_keys - .any(|verification_key| verification_key.verify_signature(&encoded_payload, &proof.1).is_ok()); - if is_signature_valid_for_revealed_keys { - Ok(()) + let valid_signing_key = proof_verification_keys + .find(|(verification_key, _)| verification_key.verify_signature(&encoded_payload, &proof.1).is_ok()); + if let Some((key, relationship)) = valid_signing_key { + proof_entry.details.bump(); + Ok((key.clone(), relationship)) } else { Err(()) } } } -pub struct KiltDipProof { - identity_proof: VersionedIdentityProof, ProofLeaf>, - did_signature: DidSignature, +pub trait DidDipOriginFilter { + type Error; + type OriginInfo; + type Success; + + fn check_call_origin_info(call: &Call, info: &Self::OriginInfo) -> Result; } -pub struct KiltDipProofVerifier( - PhantomData<(KeyId, BlockNumber, Hasher, Details, AccountId)>, +// Verifies a DID signature over the call details AND verifies whether the call +// could be dispatched with the provided signature. +pub struct DidSignatureAndCallVerifier( + PhantomData<(BlockNumber, Details, CallVerifier)>, ); -impl IdentityProofVerifier - for KiltDipProofVerifier +impl IdentityProofVerifier + for DidSignatureAndCallVerifier where - Hasher: sp_core::Hasher, + BlockNumber: Encode, Call: Encode, - Details: Encode, - AccountId: Encode, - KeyId: Encode + Clone + Ord, - BlockNumber: Encode + Clone + Ord, + CallVerifier: DidDipOriginFilter< + Call, + OriginInfo = as IdentityProofVerifier>::VerificationResult, + >, + Details: Bump + Encode, { - // TODO: Error handling + // FIXME: Better error handling type Error = (); - type Proof = KiltDipProof; - type ProofEntry = pallet_dip_consumer::proof::ProofEntry<::Out, Details>; - type Submitter = AccountId; + type Proof = as IdentityProofVerifier>::Proof; + type ProofEntry = as IdentityProofVerifier>::ProofEntry; + type Submitter = as IdentityProofVerifier>::Submitter; type VerificationResult = - as IdentityProofVerifier< - Call, - Subject, - >>::VerificationResult; + as IdentityProofVerifier>::VerificationResult; + + fn verify_proof_for_call_against_entry( + call: &Call, + subject: &Subject, + submitter: &Self::Submitter, + proof_entry: &mut Self::ProofEntry, + proof: &Self::Proof, + ) -> Result { + let did_signing_key = + DidSignatureVerifier::verify_proof_for_call_against_entry(call, subject, submitter, proof_entry, proof) + .map_err(|_| ())?; + CallVerifier::check_call_origin_info(call, &did_signing_key).map_err(|_| ())?; + Ok(did_signing_key) + } +} + +pub struct MerkleProofAndDidSignatureVerifier( + PhantomData<(MerkleProofVerifier, DidSignatureVerifier)>, +); + +impl IdentityProofVerifier + for MerkleProofAndDidSignatureVerifier +where + MerkleProofVerifier: IdentityProofVerifier + + pallet_dip_consumer::traits::IdentityProofVerifier, + DidSignatureVerifier: IdentityProofVerifier< + Call, + Subject, + Proof = >::VerificationResult, + ProofEntry = >::ProofEntry, + Submitter = >::Submitter, + >, +{ + // FIXME: Better error handling + type Error = (); + type Proof = >::Proof; + type ProofEntry = >::ProofEntry; + type Submitter = >::Submitter; + type VerificationResult = >::VerificationResult; fn verify_proof_for_call_against_entry( call: &Call, subject: &Subject, submitter: &Self::Submitter, - proof_entry: &Self::ProofEntry, - proof: Self::Proof, + proof_entry: &mut Self::ProofEntry, + proof: &Self::Proof, ) -> Result { let merkle_proof_verification = - DidMerkleProofVerifier::::verify_proof_for_call_against_entry( - call, - subject, - submitter, - proof_entry, - proof.identity_proof, - )?; - DidSignatureVerifier::::verify_proof_for_call_against_entry( + MerkleProofVerifier::verify_proof_for_call_against_entry(call, subject, submitter, proof_entry, proof) + .map_err(|_| ())?; + DidSignatureVerifier::verify_proof_for_call_against_entry( call, subject, submitter, proof_entry, - (merkle_proof_verification.0.clone(), proof.did_signature), - )?; + &merkle_proof_verification, + ) + .map_err(|_| ())?; Ok(merkle_proof_verification) } } diff --git a/runtimes/common/src/dip/mod.rs b/runtimes/common/src/dip/mod.rs index 645f1ea4d6..c1a5c47c38 100644 --- a/runtimes/common/src/dip/mod.rs +++ b/runtimes/common/src/dip/mod.rs @@ -18,7 +18,7 @@ use did::{did_details::DidPublicKeyDetails, DidVerificationKeyRelationship}; use frame_support::RuntimeDebug; -use parity_scale_codec::{Decode, Encode}; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_std::vec::Vec; @@ -28,7 +28,7 @@ pub mod provider; #[cfg(test)] mod tests; -#[derive(Clone, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord)] +#[derive(Clone, Copy, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord, MaxEncodedLen)] pub enum KeyRelationship { Encryption, Verification(DidVerificationKeyRelationship), @@ -40,6 +40,19 @@ impl From for KeyRelationship { } } +impl TryFrom for DidVerificationKeyRelationship { + // TODO: Error handling + type Error = (); + + fn try_from(value: KeyRelationship) -> Result { + if let KeyRelationship::Verification(rel) = value { + Ok(rel) + } else { + Err(()) + } + } +} + #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] pub struct KeyReferenceKey(pub KeyId, pub KeyRelationship); #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] diff --git a/runtimes/common/src/lib.rs b/runtimes/common/src/lib.rs index e94d361b43..bdc0477de2 100644 --- a/runtimes/common/src/lib.rs +++ b/runtimes/common/src/lib.rs @@ -35,7 +35,7 @@ use frame_system::limits; use pallet_transaction_payment::{Multiplier, TargetedFeeAdjustment}; use sp_runtime::{ generic, - traits::{Bounded, IdentifyAccount, Verify}, + traits::{BlakeTwo256, Bounded, IdentifyAccount, Verify}, FixedPointNumber, MultiSignature, Perquintill, SaturatedConversion, }; use sp_std::marker::PhantomData; @@ -101,8 +101,10 @@ pub type Amount = i128; /// Index of a transaction in the chain. pub type Index = u64; +/// Hasher for chain data. +pub type Hasher = BlakeTwo256; /// A hash of some data used by the chain. -pub type Hash = sp_core::H256; +pub type Hash = ::Out; /// Digest item type. pub type DigestItem = generic::DigestItem; diff --git a/runtimes/peregrine/src/lib.rs b/runtimes/peregrine/src/lib.rs index 8bedf0b7e0..eb1e10cc7c 100644 --- a/runtimes/peregrine/src/lib.rs +++ b/runtimes/peregrine/src/lib.rs @@ -62,7 +62,7 @@ use runtime_common::{ errors::PublicCredentialsApiError, fees::{ToAuthor, WeightToFee}, pallet_id, AccountId, AuthorityId, Balance, BlockHashCount, BlockLength, BlockNumber, BlockWeights, DidIdentifier, - FeeSplit, Hash, Header, Index, Signature, SlowAdjustingFeeUpdate, + FeeSplit, Hash, Hasher, Header, Index, Signature, SlowAdjustingFeeUpdate, }; use crate::xcm_config::{XcmConfig, XcmOriginToTransactDispatchOrigin}; @@ -130,7 +130,7 @@ impl frame_system::Config for Runtime { /// The type for hashing blocks and tries. type Hash = Hash; /// The hashing algorithm used. - type Hashing = BlakeTwo256; + type Hashing = Hasher; /// The header type. type Header = runtime_common::Header; /// The ubiquitous event type. diff --git a/runtimes/spiritnet/src/lib.rs b/runtimes/spiritnet/src/lib.rs index 5efb9ac4f0..796cf8e0fa 100644 --- a/runtimes/spiritnet/src/lib.rs +++ b/runtimes/spiritnet/src/lib.rs @@ -62,7 +62,7 @@ use runtime_common::{ errors::PublicCredentialsApiError, fees::{ToAuthor, WeightToFee}, pallet_id, AccountId, AuthorityId, Balance, BlockHashCount, BlockLength, BlockNumber, BlockWeights, DidIdentifier, - FeeSplit, Hash, Header, Index, Signature, SlowAdjustingFeeUpdate, + FeeSplit, Hash, Hasher, Header, Index, Signature, SlowAdjustingFeeUpdate, }; use crate::xcm_config::{XcmConfig, XcmOriginToTransactDispatchOrigin}; @@ -130,7 +130,7 @@ impl frame_system::Config for Runtime { /// The type for hashing blocks and tries. type Hash = Hash; /// The hashing algorithm used. - type Hashing = BlakeTwo256; + type Hashing = Hasher; /// The header type. type Header = runtime_common::Header; /// The ubiquitous event type. From 340470e4917775744f21b4ff4f0d56919ca12657 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Mon, 8 May 2023 16:24:06 +0200 Subject: [PATCH 09/26] Very very close --- dip-template/runtimes/dip-consumer/src/dip.rs | 27 +- runtimes/common/src/dip/consumer.rs | 43 ++- runtimes/common/src/dip/mod.rs | 3 - runtimes/common/src/dip/tests.rs | 315 ------------------ 4 files changed, 51 insertions(+), 337 deletions(-) delete mode 100644 runtimes/common/src/dip/tests.rs diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index 322d77d066..315e971766 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -16,11 +16,13 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::{did_details::DidVerificationKey, DidVerificationKeyRelationship}; +use did::{did_details::DidVerificationKey, DidSignature, DidVerificationKeyRelationship}; use dip_support::VersionedIdentityProof; use frame_support::traits::Contains; use runtime_common::dip::{ - consumer::{DidDipOriginFilter, DidMerkleProofVerifier}, + consumer::{ + DidDipOriginFilter, DidMerkleProofVerifier, DidSignatureAndCallVerifier, MerkleProofAndDidSignatureVerifier, + }, ProofLeaf, }; use sp_std::vec::Vec; @@ -30,11 +32,16 @@ use crate::{BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, Runt impl pallet_dip_consumer::Config for Runtime { type DipCallOriginFilter = PreliminaryDipOriginFilter; type Identifier = DidIdentifier; - type IdentityDetails = (); - // FIXME: Change with a proper type once everything else works. - type Proof = VersionedIdentityProof>, ProofLeaf>; + type IdentityDetails = u128; + type Proof = ( + VersionedIdentityProof>, ProofLeaf>, + DidSignature, + ); type ProofDigest = Hash; - type ProofVerifier = DidMerkleProofVerifier; + type ProofVerifier = MerkleProofAndDidSignatureVerifier< + DidMerkleProofVerifier, + DidSignatureAndCallVerifier, + >; type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; @@ -115,21 +122,21 @@ mod dip_call_origin_filter_tests { // Can call DidLookup functions with an authentication key let did_lookup_call = RuntimeCall::DidLookup(pallet_did_lookup::Call::associate_sender {}); assert_eq!( - single_key_relationship(&[&did_lookup_call]), + single_key_relationship(vec![did_lookup_call].into_iter()), Ok(DidVerificationKeyRelationship::Authentication) ); // Can't call System functions with a DID key (hence a DIP origin) let system_call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); - assert_err!(single_key_relationship(&[&system_call]), ()); + assert_err!(single_key_relationship(vec![system_call].into_iter()), ()); // Can't call empty batch with a DID key let empty_batch_call = RuntimeCall::Utility(pallet_utility::Call::batch_all { calls: vec![] }); - assert_err!(single_key_relationship(&[&empty_batch_call]), ()); + assert_err!(single_key_relationship(vec![empty_batch_call].into_iter()), ()); // Can call batch with a DipLookup with an authentication key let did_lookup_batch_call = RuntimeCall::Utility(pallet_utility::Call::batch_all { calls: vec![pallet_did_lookup::Call::associate_sender {}.into()], }); assert_eq!( - single_key_relationship(&[&did_lookup_batch_call]), + single_key_relationship(vec![did_lookup_batch_call].into_iter()), Ok(DidVerificationKeyRelationship::Authentication) ); // Can't call a batch with different required keys diff --git a/runtimes/common/src/dip/consumer.rs b/runtimes/common/src/dip/consumer.rs index be91d87126..45f3f084f2 100644 --- a/runtimes/common/src/dip/consumer.rs +++ b/runtimes/common/src/dip/consumer.rs @@ -19,7 +19,7 @@ use core::fmt::Debug; use did::{ - did_details::{DidPublicKey, DidPublicKeyDetails, DidVerificationKey}, + did_details::{DidEncryptionKey, DidPublicKey, DidPublicKeyDetails, DidVerificationKey}, DidSignature, DidVerificationKeyRelationship, }; use dip_support::{v1, VersionedIdentityProof}; @@ -45,9 +45,26 @@ pub struct ProofEntry { pub relationship: KeyRelationship, } +#[cfg(feature = "runtime-benchmarks")] +impl Default for ProofEntry +where + BlockNumber: Default, +{ + fn default() -> Self { + Self { + key: DidPublicKeyDetails { + key: DidPublicKey::PublicEncryptionKey(DidEncryptionKey::X25519([0u8; 32])), + block_number: BlockNumber::default(), + }, + relationship: DidVerificationKeyRelationship::Authentication.into(), + } + } +} + // Contains the list of revealed public keys after a given merkle proof has been // correctly verified. #[derive(Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen, Encode, Decode)] +#[cfg_attr(feature = "runtime-benchmarks", derive(Default))] pub struct VerificationResult(pub BoundedVec, ConstU32<10>>); impl From>> for VerificationResult @@ -167,7 +184,7 @@ where { // TODO: Error handling type Error = (); - type Proof = (Vec>, DidSignature); + type Proof = (VerificationResult, DidSignature); type ProofEntry = pallet_dip_consumer::proof::ProofEntry; type Submitter = AccountId; type VerificationResult = (DidVerificationKey, DidVerificationKeyRelationship); @@ -180,7 +197,7 @@ where proof: &Self::Proof, ) -> Result { let encoded_payload = (call, proof_entry.details(), submitter).encode(); - let mut proof_verification_keys = proof.0.iter().filter_map( + let mut proof_verification_keys = proof.0 .0.iter().filter_map( |ProofEntry { key: DidPublicKeyDetails { key, .. }, relationship, @@ -261,19 +278,26 @@ pub struct MerkleProofAndDidSignatureVerifier IdentityProofVerifier for MerkleProofAndDidSignatureVerifier where - MerkleProofVerifier: IdentityProofVerifier - + pallet_dip_consumer::traits::IdentityProofVerifier, + MerkleProofVerifier: IdentityProofVerifier, + MerkleProofVerifier::VerificationResult: Clone, DidSignatureVerifier: IdentityProofVerifier< Call, Subject, - Proof = >::VerificationResult, + Proof = ( + >::VerificationResult, + DidSignature, + ), ProofEntry = >::ProofEntry, Submitter = >::Submitter, >, { // FIXME: Better error handling type Error = (); - type Proof = >::Proof; + // FIXME: Better type declaration + type Proof = ( + >::Proof, + DidSignature, + ); type ProofEntry = >::ProofEntry; type Submitter = >::Submitter; type VerificationResult = >::VerificationResult; @@ -286,14 +310,15 @@ where proof: &Self::Proof, ) -> Result { let merkle_proof_verification = - MerkleProofVerifier::verify_proof_for_call_against_entry(call, subject, submitter, proof_entry, proof) + MerkleProofVerifier::verify_proof_for_call_against_entry(call, subject, submitter, proof_entry, &proof.0) .map_err(|_| ())?; DidSignatureVerifier::verify_proof_for_call_against_entry( call, subject, submitter, proof_entry, - &merkle_proof_verification, + // FIXME: Remove `clone()` requirement + &(merkle_proof_verification.clone(), proof.1.clone()), ) .map_err(|_| ())?; Ok(merkle_proof_verification) diff --git a/runtimes/common/src/dip/mod.rs b/runtimes/common/src/dip/mod.rs index c1a5c47c38..394fa06396 100644 --- a/runtimes/common/src/dip/mod.rs +++ b/runtimes/common/src/dip/mod.rs @@ -25,9 +25,6 @@ use sp_std::vec::Vec; pub mod consumer; pub mod provider; -#[cfg(test)] -mod tests; - #[derive(Clone, Copy, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord, MaxEncodedLen)] pub enum KeyRelationship { Encryption, diff --git a/runtimes/common/src/dip/tests.rs b/runtimes/common/src/dip/tests.rs deleted file mode 100644 index 9834ad7a63..0000000000 --- a/runtimes/common/src/dip/tests.rs +++ /dev/null @@ -1,315 +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 did::{ - did_details::{DidCreationDetails, DidEncryptionKey}, - DidVerificationKeyRelationship, KeyIdOf, -}; -use dip_support::latest::MerkleProof; -use frame_support::{ - assert_err, assert_ok, construct_runtime, parameter_types, traits::Everything, weights::constants::RocksDbWeight, -}; -use frame_system::{ - mocking::{MockBlock, MockUncheckedExtrinsic}, - EnsureSigned, RawOrigin, -}; -use pallet_dip_consumer::traits::IdentityProofVerifier; -use parity_scale_codec::Encode; -use sp_core::{ecdsa, ed25519, sr25519, ConstU16, ConstU32, ConstU64, Hasher, Pair}; -use sp_io::TestExternalities; -use sp_runtime::{ - testing::Header, - traits::{BlakeTwo256, IdentifyAccount, IdentityLookup}, - AccountId32, -}; -use sp_std::collections::btree_set::BTreeSet; - -use crate::dip::{ - consumer::DidMerkleProofVerifier, - provider::{CompleteMerkleProof, DidMerkleRootGenerator}, - ProofLeaf, -}; - -pub(crate) type AccountId = AccountId32; -pub(crate) type Balance = u128; -pub(crate) type Block = MockBlock; -pub(crate) type BlockNumber = u64; -pub(crate) type Hashing = BlakeTwo256; -pub(crate) type Index = u64; -pub(crate) type UncheckedExtrinsic = MockUncheckedExtrinsic; - -construct_runtime!( - pub enum TestRuntime where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system, - Balances: pallet_balances, - Did: did, - } -); - -impl frame_system::Config for TestRuntime { - type AccountData = pallet_balances::AccountData; - type AccountId = AccountId; - type BaseCallFilter = Everything; - type BlockHashCount = ConstU64<250>; - type BlockLength = (); - type BlockNumber = BlockNumber; - type BlockWeights = (); - type DbWeight = RocksDbWeight; - type Hash = ::Out; - type Hashing = Hashing; - type Header = Header; - type Index = Index; - type Lookup = IdentityLookup; - type MaxConsumers = ConstU32<16>; - type OnKilledAccount = (); - type OnNewAccount = (); - type OnSetCode = (); - type PalletInfo = PalletInfo; - type RuntimeCall = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type RuntimeOrigin = RuntimeOrigin; - type SS58Prefix = ConstU16<38>; - type SystemWeightInfo = (); - type Version = (); -} - -parameter_types! { - pub ExistentialDeposit: Balance = 500u64.into(); -} - -impl pallet_balances::Config for TestRuntime { - type AccountStore = System; - type Balance = Balance; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type MaxLocks = ConstU32<50>; - type MaxReserves = ConstU32<50>; - type ReserveIdentifier = [u8; 8]; - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); -} - -parameter_types! { - pub Deposit: Balance = 500u64.into(); - pub Fee: Balance = 500u64.into(); - pub MaxBlocksTxValidity: BlockNumber = 10u64; - #[derive(Debug, Clone, Eq, PartialEq)] - pub const MaxTotalKeyAgreementKeys: u32 = 2; -} - -impl did::DeriveDidCallAuthorizationVerificationKeyRelationship for RuntimeCall { - fn derive_verification_key_relationship(&self) -> did::DeriveDidCallKeyRelationshipResult { - Ok(DidVerificationKeyRelationship::Authentication) - } - - #[cfg(feature = "runtime-benchmarks")] - fn get_call_for_did_call_benchmark() -> Self { - RuntimeCall::System(frame_system::Call::remark { remark: vec![] }) - } -} - -impl did::Config for TestRuntime { - type Currency = Balances; - type Deposit = Deposit; - type DidIdentifier = AccountId; - type EnsureOrigin = EnsureSigned; - type Fee = Fee; - type FeeCollector = (); - type MaxBlocksTxValidity = MaxBlocksTxValidity; - type MaxNewKeyAgreementKeys = ConstU32<2>; - type MaxNumberOfServicesPerDid = ConstU32<1>; - type MaxNumberOfTypesPerService = ConstU32<1>; - type MaxNumberOfUrlsPerService = ConstU32<1>; - type MaxPublicKeysPerDid = ConstU32<5>; - type MaxServiceIdLength = ConstU32<100>; - type MaxServiceTypeLength = ConstU32<100>; - type MaxServiceUrlLength = ConstU32<100>; - type MaxTotalKeyAgreementKeys = MaxTotalKeyAgreementKeys; - type OriginSuccess = AccountId; - type RuntimeCall = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type RuntimeOrigin = RuntimeOrigin; - type WeightInfo = (); -} - -fn base_ext() -> TestExternalities { - TestExternalities::new( - frame_system::GenesisConfig::default() - .build_storage::() - .unwrap(), - ) -} - -const ALICE: AccountId = AccountId::new([1u8; 32]); - -#[test] -fn minimal_did_merkle_proof() { - base_ext().execute_with(|| { - // Give Alice some balance - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), ALICE, 1_000_000_000, 0)); - // Generate a DID for alice - let did_auth_key = ed25519::Pair::from_seed(&[100u8; 32]); - let did: AccountId = did_auth_key.public().into_account().into(); - let create_details = DidCreationDetails { - did: did.clone(), - submitter: ALICE, - new_attestation_key: None, - new_delegation_key: None, - new_key_agreement_keys: BTreeSet::new().try_into().unwrap(), - new_service_details: vec![], - }; - // Create Alice's DID with only authentication key - assert_ok!(Did::create( - RawOrigin::Signed(ALICE).into(), - Box::new(create_details.clone()), - did_auth_key.sign(&create_details.encode()).into() - )); - let did_details = Did::get_did(&did).expect("DID should be present"); - - // 1. Create the DID merkle proof revealing only the authentication key - let CompleteMerkleProof { root, proof } = DidMerkleRootGenerator::::generate_proof( - &did_details, - [did_details.authentication_key].iter(), - ) - .expect("Merkle proof generation should not fail."); - println!("{:?} - {:?} - {:?} bytes", root, proof, proof.encoded_size()); - // Verify the generated merkle proof - assert_ok!( - DidMerkleProofVerifier::, BlockNumber, Hashing>::verify_proof_against_digest( - proof.clone().into(), - root - ) - ); - - // 2. Fail to generate a Merkle proof for a key that does not exist - assert_err!( - DidMerkleRootGenerator::::generate_proof( - &did_details, - [<::Out>::default()].iter() - ), - () - ); - - // 3. Fail to verify a merkle proof with a compromised merkle root - let new_root = <::Out>::default(); - assert_err!( - DidMerkleProofVerifier::, BlockNumber, Hashing>::verify_proof_against_digest( - proof.into(), - new_root - ), - () - ); - }) -} - -#[test] -fn complete_did_merkle_proof() { - base_ext().execute_with(|| { - // Give Alice some balance - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), ALICE, 1_000_000_000, 0)); - // Generate a DID for alice - let did_auth_key = ed25519::Pair::from_seed(&[100u8; 32]); - let did_att_key = sr25519::Pair::from_seed(&[150u8; 32]); - let did_del_key = ecdsa::Pair::from_seed(&[200u8; 32]); - let enc_keys = BTreeSet::from_iter(vec![ - DidEncryptionKey::X25519([250u8; 32]), - DidEncryptionKey::X25519([251u8; 32]), - ]); - let did: AccountId = did_auth_key.public().into_account().into(); - let create_details = DidCreationDetails { - did: did.clone(), - submitter: ALICE, - new_attestation_key: Some(did_att_key.public().into()), - new_delegation_key: Some(did_del_key.public().into()), - new_key_agreement_keys: enc_keys - .try_into() - .expect("BTreeSet to BoundedBTreeSet should not fail"), - new_service_details: vec![], - }; - // Create Alice's DID with only authentication key - assert_ok!(Did::create( - RawOrigin::Signed(ALICE).into(), - Box::new(create_details.clone()), - did_auth_key.sign(&create_details.encode()).into() - )); - let did_details = Did::get_did(&did).expect("DID should be present"); - - // 1. Create the DID merkle proof revealing only the authentication key - let CompleteMerkleProof { root, proof } = DidMerkleRootGenerator::::generate_proof( - &did_details, - [did_details.authentication_key].iter(), - ) - .expect("Merkle proof generation should not fail."); - // Verify the generated merkle proof - assert_ok!( - DidMerkleProofVerifier::, BlockNumber, Hashing>::verify_proof_against_digest( - proof.into(), - root - ) - ); - - // 2. Create the DID merkle proof revealing all the keys - let CompleteMerkleProof { root, proof } = DidMerkleRootGenerator::::generate_proof( - &did_details, - [ - did_details.authentication_key, - did_details.attestation_key.unwrap(), - did_details.delegation_key.unwrap(), - ] - .iter() - .chain(did_details.key_agreement_keys.iter()), - ) - .expect("Merkle proof generation should not fail."); - // Verify the generated merkle proof - assert_ok!( - DidMerkleProofVerifier::, BlockNumber, Hashing>::verify_proof_against_digest( - proof.into(), - root - ) - ); - - // 2. Create the DID merkle proof revealing only the key reference and not the - // key ID - let CompleteMerkleProof { root, proof } = DidMerkleRootGenerator::::generate_proof( - &did_details, - [did_details.authentication_key].iter(), - ) - .expect("Merkle proof generation should not fail."); - let reference_only_authentication_leaf: Vec<_> = proof - .revealed - .into_iter() - .filter(|l| !matches!(l, ProofLeaf::KeyDetails(_, _))) - .collect(); - // Fail to verify the generated merkle proof - assert_err!( - DidMerkleProofVerifier::, BlockNumber, Hashing>::verify_proof_against_digest( - MerkleProof { - blinded: proof.blinded, - revealed: reference_only_authentication_leaf - } - .into(), - root - ), - () - ); - }) -} From 1309a2f3af3fa610479bb86de7c4cb5272a78b93 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Tue, 9 May 2023 08:09:20 +0200 Subject: [PATCH 10/26] Add genesis hash check --- dip-template/runtimes/dip-consumer/src/dip.rs | 16 ++++++-- runtimes/common/src/dip/consumer.rs | 38 +++++++++---------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index 315e971766..6653984421 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -18,17 +18,27 @@ use did::{did_details::DidVerificationKey, DidSignature, DidVerificationKeyRelationship}; use dip_support::VersionedIdentityProof; -use frame_support::traits::Contains; +use frame_support::traits::{Contains, Get}; use runtime_common::dip::{ consumer::{ - DidDipOriginFilter, DidMerkleProofVerifier, DidSignatureAndCallVerifier, MerkleProofAndDidSignatureVerifier, + DidDipOriginFilter, DidMerkleProofVerifier, DidSignatureAndCallVerifier, DidSignatureVerifier, + MerkleProofAndDidSignatureVerifier, }, ProofLeaf, }; +use sp_runtime::traits::Zero; use sp_std::vec::Vec; use crate::{BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; +pub struct GenesisProvider; + +impl Get for GenesisProvider { + fn get() -> Hash { + frame_system::Pallet::::block_hash(BlockNumber::zero()) + } +} + impl pallet_dip_consumer::Config for Runtime { type DipCallOriginFilter = PreliminaryDipOriginFilter; type Identifier = DidIdentifier; @@ -40,7 +50,7 @@ impl pallet_dip_consumer::Config for Runtime { type ProofDigest = Hash; type ProofVerifier = MerkleProofAndDidSignatureVerifier< DidMerkleProofVerifier, - DidSignatureAndCallVerifier, + DidSignatureAndCallVerifier, DipCallFilter>, >; type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; diff --git a/runtimes/common/src/dip/consumer.rs b/runtimes/common/src/dip/consumer.rs index 45f3f084f2..264f5b618c 100644 --- a/runtimes/common/src/dip/consumer.rs +++ b/runtimes/common/src/dip/consumer.rs @@ -19,11 +19,11 @@ use core::fmt::Debug; use did::{ - did_details::{DidEncryptionKey, DidPublicKey, DidPublicKeyDetails, DidVerificationKey}, + did_details::{DidPublicKey, DidPublicKeyDetails, DidVerificationKey}, DidSignature, DidVerificationKeyRelationship, }; use dip_support::{v1, VersionedIdentityProof}; -use frame_support::{BoundedVec, RuntimeDebug}; +use frame_support::{traits::Get, BoundedVec, RuntimeDebug}; use pallet_dip_consumer::traits::IdentityProofVerifier; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; @@ -173,14 +173,17 @@ where // Verifies a DID signature over the call details, which is the encoded tuple of // (call, proof_entry.details(), submitter address). -pub struct DidSignatureVerifier(PhantomData<(BlockNumber, Details)>); +pub struct DidSignatureVerifier( + PhantomData<(BlockNumber, Details, GenesisProvider)>, +); -impl IdentityProofVerifier - for DidSignatureVerifier +impl IdentityProofVerifier + for DidSignatureVerifier where BlockNumber: Encode, Call: Encode, Details: Bump + Encode, + GenesisProvider: Get, { // TODO: Error handling type Error = (); @@ -196,7 +199,7 @@ where proof_entry: &mut Self::ProofEntry, proof: &Self::Proof, ) -> Result { - let encoded_payload = (call, proof_entry.details(), submitter).encode(); + let encoded_payload = (call, proof_entry.details(), submitter, GenesisProvider::get()).encode(); let mut proof_verification_keys = proof.0 .0.iter().filter_map( |ProofEntry { key: DidPublicKeyDetails { key, .. }, @@ -233,28 +236,25 @@ pub trait DidDipOriginFilter { // Verifies a DID signature over the call details AND verifies whether the call // could be dispatched with the provided signature. -pub struct DidSignatureAndCallVerifier( - PhantomData<(BlockNumber, Details, CallVerifier)>, +pub struct DidSignatureAndCallVerifier( + PhantomData<(DidSignatureVerifier, CallVerifier)>, ); -impl IdentityProofVerifier - for DidSignatureAndCallVerifier +impl IdentityProofVerifier + for DidSignatureAndCallVerifier where - BlockNumber: Encode, - Call: Encode, + DidSignatureVerifier: IdentityProofVerifier, CallVerifier: DidDipOriginFilter< Call, - OriginInfo = as IdentityProofVerifier>::VerificationResult, + OriginInfo = >::VerificationResult, >, - Details: Bump + Encode, { // FIXME: Better error handling type Error = (); - type Proof = as IdentityProofVerifier>::Proof; - type ProofEntry = as IdentityProofVerifier>::ProofEntry; - type Submitter = as IdentityProofVerifier>::Submitter; - type VerificationResult = - as IdentityProofVerifier>::VerificationResult; + type Proof = >::Proof; + type ProofEntry = >::ProofEntry; + type Submitter = >::Submitter; + type VerificationResult = >::VerificationResult; fn verify_proof_for_call_against_entry( call: &Call, From 1c1fc471a5227cd2f09e141e778d616f8bd00363 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Tue, 9 May 2023 08:27:27 +0200 Subject: [PATCH 11/26] Tests compiling, not passing yet --- dip-template/runtimes/dip-consumer/src/dip.rs | 10 +++++----- dip-template/runtimes/xcm-tests/src/tests.rs | 13 ++++++++----- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index 6653984421..bc60622361 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -132,21 +132,21 @@ mod dip_call_origin_filter_tests { // Can call DidLookup functions with an authentication key let did_lookup_call = RuntimeCall::DidLookup(pallet_did_lookup::Call::associate_sender {}); assert_eq!( - single_key_relationship(vec![did_lookup_call].into_iter()), + single_key_relationship(vec![did_lookup_call].iter()), Ok(DidVerificationKeyRelationship::Authentication) ); // Can't call System functions with a DID key (hence a DIP origin) let system_call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); - assert_err!(single_key_relationship(vec![system_call].into_iter()), ()); + assert_err!(single_key_relationship(vec![system_call].iter()), ()); // Can't call empty batch with a DID key let empty_batch_call = RuntimeCall::Utility(pallet_utility::Call::batch_all { calls: vec![] }); - assert_err!(single_key_relationship(vec![empty_batch_call].into_iter()), ()); + assert_err!(single_key_relationship(vec![empty_batch_call].iter()), ()); // Can call batch with a DipLookup with an authentication key let did_lookup_batch_call = RuntimeCall::Utility(pallet_utility::Call::batch_all { calls: vec![pallet_did_lookup::Call::associate_sender {}.into()], }); assert_eq!( - single_key_relationship(vec![did_lookup_batch_call].into_iter()), + single_key_relationship(vec![did_lookup_batch_call].iter()), Ok(DidVerificationKeyRelationship::Authentication) ); // Can't call a batch with different required keys @@ -158,6 +158,6 @@ mod dip_call_origin_filter_tests { frame_system::Call::remark { remark: vec![] }.into(), ], }); - assert_err!(single_key_relationship(&[&did_lookup_batch_call]), ()); + assert_err!(single_key_relationship(vec![did_lookup_batch_call].iter()), ()); } } diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index c2b07da8b3..ba34f057c3 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -85,11 +85,14 @@ fn commit_identity() { assert_ok!(DipConsumer::dispatch_as( RawOrigin::Signed(para::consumer::DISPATCHER_ACCOUNT).into(), did.clone(), - MerkleProof { - blinded: proof.blinded, - revealed: proof.revealed, - } - .into(), + ( + MerkleProof { + blinded: proof.blinded, + revealed: proof.revealed, + } + .into(), + para::provider::did_auth_key().sign(&[0u8]).into() + ), Box::new(ConsumerRuntimeCall::DidLookup(pallet_did_lookup::Call::< ConsumerRuntime, >::associate_sender {})), From 48e7dd355c845ff05242d5d0664235cd9bfcb882 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Tue, 9 May 2023 08:45:52 +0200 Subject: [PATCH 12/26] Fixed XCM integration test! --- dip-template/runtimes/xcm-tests/src/tests.rs | 30 ++++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index ba34f057c3..4211150204 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -18,13 +18,15 @@ use super::*; -use did::Did; +use did::{Did, DidSignature}; use dip_support::latest::MerkleProof; use frame_support::{assert_ok, weights::Weight}; use frame_system::RawOrigin; use pallet_did_lookup::linkable_account::LinkableAccountId; +use parity_scale_codec::Encode; use runtime_common::dip::provider::{CompleteMerkleProof, DidMerkleRootGenerator}; use sp_core::Pair; +use sp_runtime::traits::Zero; use xcm::latest::{ Junction::Parachain, Junctions::{Here, X1}, @@ -34,7 +36,7 @@ use xcm_emulator::TestExt; use cumulus_pallet_xcmp_queue::Event as XcmpEvent; use dip_consumer_runtime_template::{ - DidIdentifier, DidLookup, DipConsumer, Runtime as ConsumerRuntime, RuntimeCall as ConsumerRuntimeCall, + BlockNumber, DidIdentifier, DidLookup, DipConsumer, Runtime as ConsumerRuntime, RuntimeCall as ConsumerRuntimeCall, RuntimeEvent, System, }; use dip_provider_runtime_template::{AccountId as ProviderAccountId, DipProvider, Runtime as ProviderRuntime}; @@ -69,17 +71,26 @@ fn commit_identity() { // 2.2 Verify the proof digest was stored correctly. assert!(DipConsumer::identity_proofs(&did).is_some()); }); - // 3. Call an extrinsic on the consumer chain with a valid proof + // 3. Call an extrinsic on the consumer chain with a valid proof and signature let did_details = ProviderParachain::execute_with(|| { Did::get(&did).expect("DID details should be stored on the provider chain.") }); + let call = ConsumerRuntimeCall::DidLookup(pallet_did_lookup::Call::::associate_sender {}); // 3.1 Generate a proof let CompleteMerkleProof { proof, .. } = DidMerkleRootGenerator::::generate_proof( &did_details, [did_details.authentication_key].iter(), ) .expect("Proof generation should not fail"); - // 3.2 Call the `dispatch_as` extrinsic on the consumer chain with the generated + // 3.2 Generate a DID signature + let payload = ( + call.clone(), + 0u128, + para::consumer::DISPATCHER_ACCOUNT, + ConsumerParachain::execute_with(|| frame_system::Pallet::::block_hash(BlockNumber::zero())), + ); + let signature: DidSignature = para::provider::did_auth_key().sign(&payload.encode()).into(); + // 3.3 Call the `dispatch_as` extrinsic on the consumer chain with the generated // proof ConsumerParachain::execute_with(|| { assert_ok!(DipConsumer::dispatch_as( @@ -91,15 +102,16 @@ fn commit_identity() { revealed: proof.revealed, } .into(), - para::provider::did_auth_key().sign(&[0u8]).into() + signature ), - Box::new(ConsumerRuntimeCall::DidLookup(pallet_did_lookup::Call::< - ConsumerRuntime, - >::associate_sender {})), + Box::new(call), )); // Verify the account -> DID link exists and contains the right information let linked_did = DidLookup::connected_dids::(para::consumer::DISPATCHER_ACCOUNT.into()) .map(|link| link.did); - assert_eq!(linked_did, Some(did)); + assert_eq!(linked_did, Some(did.clone())); + // Verify that the details of the DID subject have been bumped + let details = DipConsumer::identity_proofs(&did).map(|entry| entry.details); + assert_eq!(details, Some(1u128)); }); } From b79589c9d1a8441514764e71005fd4f4047e0f26 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Tue, 9 May 2023 10:49:54 +0200 Subject: [PATCH 13/26] Refactor complete and compiling --- Cargo.lock | 19 +- Cargo.toml | 1 + crates/dip-support/Cargo.toml | 4 +- crates/dip-support/src/lib.rs | 46 ---- crates/dip-support/src/v1.rs | 10 +- crates/kilt-dip-support/Cargo.toml | 42 ++++ .../kilt-dip-support/src/did.rs | 182 ++------------ crates/kilt-dip-support/src/lib.rs | 25 ++ crates/kilt-dip-support/src/merkle.rs | 224 ++++++++++++++++++ crates/kilt-dip-support/src/traits.rs | 45 ++++ dip-template/runtimes/dip-consumer/Cargo.toml | 2 + dip-template/runtimes/dip-consumer/src/dip.rs | 28 +-- dip-template/runtimes/dip-consumer/src/lib.rs | 6 +- dip-template/runtimes/dip-provider/src/dip.rs | 8 +- dip-template/runtimes/dip-provider/src/lib.rs | 6 +- pallets/pallet-dip-consumer/src/lib.rs | 9 +- pallets/pallet-dip-provider/src/lib.rs | 19 +- pallets/pallet-dip-provider/src/traits.rs | 10 +- runtimes/common/Cargo.toml | 4 +- runtimes/common/src/dip/did.rs | 42 ++++ .../common/src/dip/{provider.rs => merkle.rs} | 40 +--- runtimes/common/src/dip/mod.rs | 76 +----- 22 files changed, 470 insertions(+), 378 deletions(-) create mode 100644 crates/kilt-dip-support/Cargo.toml rename runtimes/common/src/dip/consumer.rs => crates/kilt-dip-support/src/did.rs (50%) create mode 100644 crates/kilt-dip-support/src/lib.rs create mode 100644 crates/kilt-dip-support/src/merkle.rs create mode 100644 crates/kilt-dip-support/src/traits.rs create mode 100644 runtimes/common/src/dip/did.rs rename runtimes/common/src/dip/{provider.rs => merkle.rs} (87%) diff --git a/Cargo.lock b/Cargo.lock index 497ab5d25c..8858a9c17c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2283,6 +2283,7 @@ dependencies = [ "frame-support", "frame-system", "frame-system-rpc-runtime-api", + "kilt-dip-support", "pallet-aura", "pallet-authorship", "pallet-balances", @@ -2425,7 +2426,6 @@ dependencies = [ "frame-support", "parity-scale-codec", "scale-info", - "sp-std", ] [[package]] @@ -4149,6 +4149,21 @@ dependencies = [ "sp-std", ] +[[package]] +name = "kilt-dip-support" +version = "1.11.0-dev" +dependencies = [ + "did", + "frame-support", + "pallet-dip-consumer", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", + "sp-trie", +] + [[package]] name = "kilt-parachain" version = "1.11.0-dev" @@ -9313,10 +9328,10 @@ dependencies = [ "cumulus-primitives-core", "delegation", "did", - "dip-support", "frame-support", "frame-system", "kilt-asset-dids", + "kilt-dip-support", "kilt-support", "log", "pallet-authorship", diff --git a/Cargo.toml b/Cargo.toml index d912c160e1..4be4c260c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,6 +62,7 @@ public-credentials = {path = "pallets/public-credentials", default-features = fa # Internal support (with default disabled) dip-support = {path = "crates/dip-support", default-features = false} kilt-asset-dids = {path = "crates/assets", default-features = false} +kilt-dip-support = {path = "crates/kilt-dip-support", default-features = false} kilt-support = {path = "support", default-features = false} runtime-common = {path = "runtimes/common", default-features = false} diff --git a/crates/dip-support/Cargo.toml b/crates/dip-support/Cargo.toml index d09a0e5413..276b3929ac 100644 --- a/crates/dip-support/Cargo.toml +++ b/crates/dip-support/Cargo.toml @@ -17,13 +17,11 @@ scale-info = {workspace = true, features = ["derive"]} # Substrate dependencies frame-support.workspace = true -sp-std.workspace = true [features] default = ["std"] std = [ "parity-scale-codec/std", "scale-info/std", - "frame-support/std", - "sp-std/std" + "frame-support/std" ] diff --git a/crates/dip-support/src/lib.rs b/crates/dip-support/src/lib.rs index c0ff96f0eb..fa388d032e 100644 --- a/crates/dip-support/src/lib.rs +++ b/crates/dip-support/src/lib.rs @@ -20,54 +20,8 @@ #![cfg_attr(not(feature = "std"), no_std)] -use frame_support::RuntimeDebug; -use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; - // Export v1 behind a namespace and also as the latest pub mod v1; pub mod latest { pub use crate::v1::*; } - -#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] -#[non_exhaustive] -pub enum VersionedIdentityProofAction { - #[codec(index = 1)] - V1(v1::IdentityProofAction), -} - -impl From> - for VersionedIdentityProofAction -{ - fn from(value: v1::IdentityProofAction) -> Self { - Self::V1(value) - } -} - -#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)] -#[non_exhaustive] -pub enum VersionedIdentityProof { - #[codec(index = 1)] - V1(v1::MerkleProof), -} - -impl From> for VersionedIdentityProof { - fn from(value: v1::MerkleProof) -> Self { - Self::V1(value) - } -} - -impl TryFrom> for v1::MerkleProof { - // Proper error handling - type Error = (); - - fn try_from(value: VersionedIdentityProof) -> Result { - #[allow(irrefutable_let_patterns)] - if let VersionedIdentityProof::V1(v1::MerkleProof { blinded, revealed }) = value { - Ok(Self { blinded, revealed }) - } else { - Err(()) - } - } -} diff --git a/crates/dip-support/src/v1.rs b/crates/dip-support/src/v1.rs index f4ff7fab51..243e3c1561 100644 --- a/crates/dip-support/src/v1.rs +++ b/crates/dip-support/src/v1.rs @@ -19,17 +19,9 @@ use frame_support::RuntimeDebug; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; -use sp_std::vec::Vec; -#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] +#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen, RuntimeDebug)] pub enum IdentityProofAction { Updated(Identifier, Proof, Details), Deleted(Identifier), } - -#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo, Default)] -pub struct MerkleProof { - pub blinded: BlindedValue, - // TODO: Probably replace with a different data structure for better lookup capabilities - pub revealed: Vec, -} diff --git a/crates/kilt-dip-support/Cargo.toml b/crates/kilt-dip-support/Cargo.toml new file mode 100644 index 0000000000..7f879b09f3 --- /dev/null +++ b/crates/kilt-dip-support/Cargo.toml @@ -0,0 +1,42 @@ +[package] +authors.workspace = true +description = "Support types, traits, and functions for the KILT Decentralized Identity Provider (DIP) functionality as implemented by the KILT blockchain." +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +name = "kilt-dip-support" +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +# Internal dependencies +did.workspace = true +pallet-dip-consumer.workspace = true + +# Parity dependencies +parity-scale-codec = {workspace = true, features = ["derive"]} +scale-info = {workspace = true, features = ["derive"]} + +# Substrate dependencies +frame-support.workspace = true +sp-runtime.workspace = true +sp-core.workspace = true +sp-trie.workspace = true +sp-std.workspace = true + +[features] +default = ["std"] +std = [ + "did/std", + "pallet-dip-consumer/std", + "parity-scale-codec/std", + "scale-info/std", + "frame-support/std", + "sp-runtime/std", + "sp-core/std", + "sp-trie/std", + "sp-std/std" +] +runtime-benchmarks = [] diff --git a/runtimes/common/src/dip/consumer.rs b/crates/kilt-dip-support/src/did.rs similarity index 50% rename from runtimes/common/src/dip/consumer.rs rename to crates/kilt-dip-support/src/did.rs index 264f5b618c..8db87b2c4a 100644 --- a/runtimes/common/src/dip/consumer.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -16,179 +16,40 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use core::fmt::Debug; - use did::{ did_details::{DidPublicKey, DidPublicKeyDetails, DidVerificationKey}, DidSignature, DidVerificationKeyRelationship, }; -use dip_support::{v1, VersionedIdentityProof}; -use frame_support::{traits::Get, BoundedVec, RuntimeDebug}; use pallet_dip_consumer::traits::IdentityProofVerifier; -use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; -use sp_core::ConstU32; -use sp_runtime::traits::{CheckedAdd, One, Zero}; -use sp_std::{collections::btree_map::BTreeMap, marker::PhantomData, vec::Vec}; -use sp_trie::{verify_trie_proof, LayoutV1}; +use parity_scale_codec::Encode; +use sp_core::{ConstU32, Get}; +use sp_std::marker::PhantomData; use crate::{ - dip::{provider, KeyDetailsKey, KeyDetailsValue, KeyReferenceKey, KeyRelationship, ProofLeaf}, - AccountId, Hash, + merkle::{ProofEntry, VerificationResult}, + traits::{Bump, DidDipOriginFilter}, }; -// TODO: Avoid repetition of the same key if it appears multiple times, e.g., by -// having a vector of `KeyRelationship` instead. -#[derive(Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen, Encode, Decode)] -pub struct ProofEntry { - pub key: DidPublicKeyDetails, - pub relationship: KeyRelationship, -} - -#[cfg(feature = "runtime-benchmarks")] -impl Default for ProofEntry -where - BlockNumber: Default, -{ - fn default() -> Self { - Self { - key: DidPublicKeyDetails { - key: DidPublicKey::PublicEncryptionKey(DidEncryptionKey::X25519([0u8; 32])), - block_number: BlockNumber::default(), - }, - relationship: DidVerificationKeyRelationship::Authentication.into(), - } - } -} - -// Contains the list of revealed public keys after a given merkle proof has been -// correctly verified. -#[derive(Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen, Encode, Decode)] -#[cfg_attr(feature = "runtime-benchmarks", derive(Default))] -pub struct VerificationResult(pub BoundedVec, ConstU32<10>>); - -impl From>> for VerificationResult -where - BlockNumber: Debug, -{ - fn from(value: Vec>) -> Self { - Self(value.try_into().expect("Failed to put Vec into BoundedVec")) - } -} - -pub struct DidMerkleProofVerifier(PhantomData<(Hasher, BlockNumber, Details)>); - -impl IdentityProofVerifier - for DidMerkleProofVerifier -where - BlockNumber: Encode + Clone + Debug, - Hasher: sp_core::Hasher, - Hasher::Out: From, -{ - // TODO: Proper error handling - type Error = (); - type Proof = VersionedIdentityProof, ProofLeaf>; - type ProofEntry = pallet_dip_consumer::proof::ProofEntry; - type Submitter = AccountId; - type VerificationResult = VerificationResult; - - fn verify_proof_for_call_against_entry( - _call: &Call, - _subject: &Subject, - _submitter: &Self::Submitter, - proof_entry: &mut Self::ProofEntry, - proof: &Self::Proof, - ) -> Result { - let proof: v1::MerkleProof<_, _> = proof.clone().try_into()?; - // TODO: more efficient by removing cloning and/or collecting. - // Did not find another way of mapping a Vec<(Vec, Vec)> to a - // Vec<(Vec, Option>)>. - let proof_leaves = proof - .revealed - .iter() - .map(|leaf| (leaf.encoded_key(), Some(leaf.encoded_value()))) - .collect::, Option>)>>(); - verify_trie_proof::, _, _, _>(&proof_entry.digest.into(), &proof.blinded, &proof_leaves) - .map_err(|_| ())?; - - // At this point, we know the proof is valid. We just need to map the revealed - // leaves to something the consumer can easily operate on. - - // Create a map of the revealed public keys - //TODO: Avoid cloning, and use a map of references for the lookup - let public_keys: BTreeMap> = proof - .revealed - .clone() - .into_iter() - .filter_map(|leaf| { - if let ProofLeaf::KeyDetails(KeyDetailsKey(key_id), KeyDetailsValue(key_details)) = leaf { - Some((key_id, key_details)) - } else { - None - } - }) - .collect(); - // Create a list of the revealed keys by consuming the provided key reference - // leaves, and looking up the full details from the just-built `public_keys` - // map. - let keys: Vec> = proof - .revealed - .into_iter() - .filter_map(|leaf| { - if let ProofLeaf::KeyReference(KeyReferenceKey(key_id, key_relationship), _) = leaf { - // TODO: Better error handling. - let key_details = public_keys - .get(&key_id) - .expect("Key ID should be present in the map of revealed public keys."); - Some(ProofEntry { - key: key_details.clone(), - relationship: key_relationship, - }) - } else { - None - } - }) - .collect(); - Ok(keys.into()) - } -} - -pub trait Bump { - fn bump(&mut self); -} - -impl Bump for T -where - T: CheckedAdd + Zero + One, -{ - // FIXME: Better implementation? - fn bump(&mut self) { - if let Some(new) = self.checked_add(&Self::one()) { - *self = new; - } else { - *self = Self::zero(); - } - } -} - -// Verifies a DID signature over the call details, which is the encoded tuple of -// (call, proof_entry.details(), submitter address). -pub struct DidSignatureVerifier( - PhantomData<(BlockNumber, Details, GenesisProvider)>, +pub struct DidSignatureVerifier( + PhantomData<(BlockNumber, Digest, Details, AccountId, SignedExtraProvider, S, L)>, ); -impl IdentityProofVerifier - for DidSignatureVerifier +impl + IdentityProofVerifier + for DidSignatureVerifier> where + AccountId: Encode, BlockNumber: Encode, Call: Encode, + Digest: Encode, Details: Bump + Encode, - GenesisProvider: Get, + SignedExtraProvider: Get, + S: Encode, { // TODO: Error handling type Error = (); - type Proof = (VerificationResult, DidSignature); - type ProofEntry = pallet_dip_consumer::proof::ProofEntry; + type Proof = (VerificationResult, DidSignature); + type ProofEntry = pallet_dip_consumer::proof::ProofEntry; type Submitter = AccountId; type VerificationResult = (DidVerificationKey, DidVerificationKeyRelationship); @@ -199,7 +60,7 @@ where proof_entry: &mut Self::ProofEntry, proof: &Self::Proof, ) -> Result { - let encoded_payload = (call, proof_entry.details(), submitter, GenesisProvider::get()).encode(); + let encoded_payload = (call, proof_entry.details(), submitter, SignedExtraProvider::get()).encode(); let mut proof_verification_keys = proof.0 .0.iter().filter_map( |ProofEntry { key: DidPublicKeyDetails { key, .. }, @@ -226,14 +87,6 @@ where } } -pub trait DidDipOriginFilter { - type Error; - type OriginInfo; - type Success; - - fn check_call_origin_info(call: &Call, info: &Self::OriginInfo) -> Result; -} - // Verifies a DID signature over the call details AND verifies whether the call // could be dispatched with the provided signature. pub struct DidSignatureAndCallVerifier( @@ -279,6 +132,7 @@ impl IdentityProofVeri for MerkleProofAndDidSignatureVerifier where MerkleProofVerifier: IdentityProofVerifier, + // TODO: get rid of this if possible MerkleProofVerifier::VerificationResult: Clone, DidSignatureVerifier: IdentityProofVerifier< Call, diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-support/src/lib.rs new file mode 100644 index 0000000000..7e8d878e79 --- /dev/null +++ b/crates/kilt-dip-support/src/lib.rs @@ -0,0 +1,25 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +// TODO: Crate documentation + +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod did; +pub mod merkle; +pub mod traits; diff --git a/crates/kilt-dip-support/src/merkle.rs b/crates/kilt-dip-support/src/merkle.rs new file mode 100644 index 0000000000..9b4408094b --- /dev/null +++ b/crates/kilt-dip-support/src/merkle.rs @@ -0,0 +1,224 @@ +// 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 did::{did_details::DidPublicKeyDetails, DidVerificationKeyRelationship}; +use frame_support::{traits::ConstU32, RuntimeDebug}; +use pallet_dip_consumer::traits::IdentityProofVerifier; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_runtime::BoundedVec; +use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, marker::PhantomData, vec::Vec}; +use sp_trie::{verify_trie_proof, LayoutV1}; + +pub type BlindedValue = Vec; + +#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo, Default)] +pub struct MerkleProof { + pub blinded: BlindedValue, + // TODO: Probably replace with a different data structure for better lookup capabilities + pub revealed: Vec, +} + +#[derive(Clone, Copy, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord, MaxEncodedLen)] +pub enum DidKeyRelationship { + Encryption, + Verification(DidVerificationKeyRelationship), +} + +impl From for DidKeyRelationship { + fn from(value: DidVerificationKeyRelationship) -> Self { + Self::Verification(value) + } +} + +impl TryFrom for DidVerificationKeyRelationship { + // TODO: Error handling + type Error = (); + + fn try_from(value: DidKeyRelationship) -> Result { + if let DidKeyRelationship::Verification(rel) = value { + Ok(rel) + } else { + Err(()) + } + } +} + +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] +pub struct KeyReferenceKey(pub KeyId, pub DidKeyRelationship); +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] +pub struct KeyReferenceValue; +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] +pub struct KeyDetailsKey(pub KeyId); +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] +pub struct KeyDetailsValue(pub DidPublicKeyDetails); + +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] +pub enum ProofLeaf { + // 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. + KeyReference(KeyReferenceKey, KeyReferenceValue), + // The key and value for the leaves of a merkle proof that contain the actual + // details of a DID public key. The key is the ID of the key, and the value is its details, including creation + // block number. + KeyDetails(KeyDetailsKey, KeyDetailsValue), +} + +impl ProofLeaf +where + KeyId: Encode, + BlockNumber: Encode, +{ + pub fn encoded_key(&self) -> Vec { + match self { + ProofLeaf::KeyReference(key, _) => key.encode(), + ProofLeaf::KeyDetails(key, _) => key.encode(), + } + } + + pub fn encoded_value(&self) -> Vec { + match self { + ProofLeaf::KeyReference(_, value) => value.encode(), + ProofLeaf::KeyDetails(_, value) => value.encode(), + } + } +} + +// TODO: Avoid repetition of the same key if it appears multiple times, e.g., by +// having a vector of `DidKeyRelationship` instead. +#[derive(Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen, Encode, Decode)] +pub struct ProofEntry { + pub key: DidPublicKeyDetails, + pub relationship: DidKeyRelationship, +} + +#[cfg(feature = "runtime-benchmarks")] +impl Default for ProofEntry +where + BlockNumber: Default, +{ + fn default() -> Self { + Self { + key: DidPublicKeyDetails { + key: DidPublicKey::PublicEncryptionKey(DidEncryptionKey::X25519([0u8; 32])), + block_number: BlockNumber::default(), + }, + relationship: DidVerificationKeyRelationship::Authentication.into(), + } + } +} + +// Contains the list of revealed public keys after a given merkle proof has been +// correctly verified. +#[derive(Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen, Encode, Decode)] +#[cfg_attr(feature = "runtime-benchmarks", derive(Default))] +pub struct VerificationResult(pub BoundedVec, ConstU32>); + +impl From>> for VerificationResult +where + // TODO: Remove + // this bound + BlockNumber: Debug, +{ + fn from(value: Vec>) -> Self { + Self(value.try_into().expect("Failed to put Vec into BoundedVec")) + } +} + +pub struct DidMerkleProofVerifier( + PhantomData<(Hasher, AccountId, KeyId, BlockNumber, Details, ConstU32)>, +); + +impl IdentityProofVerifier + for DidMerkleProofVerifier +where + // TODO: Remove `Debug` bound + BlockNumber: Encode + Clone + Debug, + Hasher: sp_core::Hasher, + KeyId: Encode + Clone + Ord + Into, +{ + // TODO: Proper error handling + type Error = (); + type Proof = MerkleProof>, ProofLeaf>; + type ProofEntry = pallet_dip_consumer::proof::ProofEntry; + type Submitter = AccountId; + type VerificationResult = VerificationResult; + + fn verify_proof_for_call_against_entry( + _call: &Call, + _subject: &Subject, + _submitter: &Self::Submitter, + proof_entry: &mut Self::ProofEntry, + proof: &Self::Proof, + ) -> Result { + // TODO: more efficient by removing cloning and/or collecting. + // Did not find another way of mapping a Vec<(Vec, Vec)> to a + // Vec<(Vec, Option>)>. + let proof_leaves = proof + .revealed + .iter() + .map(|leaf| (leaf.encoded_key(), Some(leaf.encoded_value()))) + .collect::, Option>)>>(); + verify_trie_proof::, _, _, _>( + &proof_entry.digest.clone().into(), + &proof.blinded, + &proof_leaves, + ) + .map_err(|_| ())?; + + // At this point, we know the proof is valid. We just need to map the revealed + // leaves to something the consumer can easily operate on. + + // Create a map of the revealed public keys + //TODO: Avoid cloning, and use a map of references for the lookup + let public_keys: BTreeMap> = proof + .revealed + .clone() + .into_iter() + .filter_map(|leaf| { + if let ProofLeaf::KeyDetails(KeyDetailsKey(key_id), KeyDetailsValue(key_details)) = leaf { + Some((key_id, key_details)) + } else { + None + } + }) + .collect(); + // Create a list of the revealed keys by consuming the provided key reference + // leaves, and looking up the full details from the just-built `public_keys` + // map. + let keys: Vec> = proof + .revealed + .iter() + .filter_map(|leaf| { + if let ProofLeaf::KeyReference(KeyReferenceKey(key_id, key_relationship), _) = leaf { + // TODO: Better error handling. + let key_details = public_keys + .get(key_id) + .expect("Key ID should be present in the map of revealed public keys."); + Some(ProofEntry { + key: key_details.clone(), + relationship: *key_relationship, + }) + } else { + None + } + }) + .collect(); + Ok(keys.into()) + } +} diff --git a/crates/kilt-dip-support/src/traits.rs b/crates/kilt-dip-support/src/traits.rs new file mode 100644 index 0000000000..45dc099876 --- /dev/null +++ b/crates/kilt-dip-support/src/traits.rs @@ -0,0 +1,45 @@ +// 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 sp_runtime::traits::{CheckedAdd, One, Zero}; + +pub trait Bump { + fn bump(&mut self); +} + +impl Bump for T +where + T: CheckedAdd + Zero + One, +{ + // FIXME: Better implementation? + fn bump(&mut self) { + if let Some(new) = self.checked_add(&Self::one()) { + *self = new; + } else { + *self = Self::zero(); + } + } +} + +pub trait DidDipOriginFilter { + type Error; + type OriginInfo; + type Success; + + fn check_call_origin_info(call: &Call, info: &Self::OriginInfo) -> Result; +} diff --git a/dip-template/runtimes/dip-consumer/Cargo.toml b/dip-template/runtimes/dip-consumer/Cargo.toml index 1135dba2ad..de8f00d270 100644 --- a/dip-template/runtimes/dip-consumer/Cargo.toml +++ b/dip-template/runtimes/dip-consumer/Cargo.toml @@ -20,6 +20,7 @@ scale-info = {workspace = true, features = ["derive"]} # DIP dip-support.workspace = true did.workspace = true +kilt-dip-support.workspace = true pallet-did-lookup.workspace = true pallet-dip-consumer.workspace = true runtime-common.workspace = true @@ -78,6 +79,7 @@ std = [ "scale-info/std", "dip-support/std", "did/std", + "kilt-dip-support/std", "pallet-did-lookup/std", "pallet-dip-consumer/std", "runtime-common/std", diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index bc60622361..3ad06e09d3 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -16,20 +16,18 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::{did_details::DidVerificationKey, DidSignature, DidVerificationKeyRelationship}; -use dip_support::VersionedIdentityProof; +use did::{did_details::DidVerificationKey, DidSignature, DidVerificationKeyRelationship, KeyIdOf}; use frame_support::traits::{Contains, Get}; -use runtime_common::dip::{ - consumer::{ - DidDipOriginFilter, DidMerkleProofVerifier, DidSignatureAndCallVerifier, DidSignatureVerifier, - MerkleProofAndDidSignatureVerifier, - }, - ProofLeaf, +use kilt_dip_support::{ + did::{DidSignatureAndCallVerifier, DidSignatureVerifier, MerkleProofAndDidSignatureVerifier}, + merkle::{DidMerkleProofVerifier, MerkleProof, ProofLeaf}, + traits::DidDipOriginFilter, }; +use sp_core::ConstU32; use sp_runtime::traits::Zero; use sp_std::vec::Vec; -use crate::{BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; +use crate::{AccountId, BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; pub struct GenesisProvider; @@ -43,14 +41,14 @@ impl pallet_dip_consumer::Config for Runtime { type DipCallOriginFilter = PreliminaryDipOriginFilter; type Identifier = DidIdentifier; type IdentityDetails = u128; - type Proof = ( - VersionedIdentityProof>, ProofLeaf>, - DidSignature, - ); + type Proof = (MerkleProof>, ProofLeaf>, DidSignature); type ProofDigest = Hash; type ProofVerifier = MerkleProofAndDidSignatureVerifier< - DidMerkleProofVerifier, - DidSignatureAndCallVerifier, DipCallFilter>, + DidMerkleProofVerifier, BlockNumber, u128, 10>, + DidSignatureAndCallVerifier< + DidSignatureVerifier>, + DipCallFilter, + >, >; type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; diff --git a/dip-template/runtimes/dip-consumer/src/lib.rs b/dip-template/runtimes/dip-consumer/src/lib.rs index 19b86e5c14..602e4af539 100644 --- a/dip-template/runtimes/dip-consumer/src/lib.rs +++ b/dip-template/runtimes/dip-consumer/src/lib.rs @@ -22,7 +22,7 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); -use runtime_common::dip::consumer::VerificationResult; +use kilt_dip_support::merkle::VerificationResult; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::{MultiAddress, Perbill, Permill}; @@ -367,8 +367,8 @@ impl pallet_did_lookup::Config for Runtime { type Currency = Balances; type Deposit = ConstU128; type DidIdentifier = DidIdentifier; - type EnsureOrigin = EnsureDipOrigin>; - type OriginSuccess = DipOrigin>; + type EnsureOrigin = EnsureDipOrigin>; + type OriginSuccess = DipOrigin>; type RuntimeEvent = RuntimeEvent; type WeightInfo = (); } diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index 6fa0d577bd..82c151182c 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -17,10 +17,10 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use did::did_details::DidDetails; -use dip_support::VersionedIdentityProofAction; +use dip_support::latest::IdentityProofAction; use pallet_dip_provider::traits::{TxBuilder, XcmRouterDispatcher}; use parity_scale_codec::{Decode, Encode}; -use runtime_common::dip::provider::{DidIdentityProvider, DidMerkleRootGenerator}; +use runtime_common::dip::{did::DidIdentityProvider, merkle::DidMerkleRootGenerator}; use xcm::{latest::MultiLocation, DoubleEncoded}; use crate::{DidIdentifier, Hash, Runtime, RuntimeEvent, XcmRouter}; @@ -34,7 +34,7 @@ enum ConsumerParachainCalls { #[derive(Encode, Decode)] enum ConsumerParachainDipConsumerCalls { #[codec(index = 0)] - ProcessIdentityAction(VersionedIdentityProofAction), + ProcessIdentityAction(IdentityProofAction), } pub struct ConsumerParachainTxBuilder; @@ -43,7 +43,7 @@ impl TxBuilder for ConsumerParachainTxBuilder { fn build( _dest: MultiLocation, - action: VersionedIdentityProofAction, + action: IdentityProofAction, ) -> Result, Self::Error> { let double_encoded: DoubleEncoded<()> = ConsumerParachainCalls::DipConsumer(ConsumerParachainDipConsumerCalls::ProcessIdentityAction(action)) diff --git a/dip-template/runtimes/dip-provider/src/lib.rs b/dip-template/runtimes/dip-provider/src/lib.rs index d1db46563a..b3766aa160 100644 --- a/dip-template/runtimes/dip-provider/src/lib.rs +++ b/dip-template/runtimes/dip-provider/src/lib.rs @@ -51,7 +51,7 @@ use pallet_collator_selection::IdentityCollator; use pallet_dip_provider::traits::IdentityProvider; use pallet_session::{FindAccountFromAuthorIndex, PeriodicSessions}; use pallet_transaction_payment::{CurrencyAdapter, FeeDetails, RuntimeDispatchInfo}; -use runtime_common::dip::provider::{CompleteMerkleProof, DidMerkleProof, DidMerkleRootGenerator}; +use runtime_common::dip::merkle::{CompleteMerkleProof, DidMerkleProofOf, DidMerkleRootGenerator}; use sp_api::impl_runtime_apis; use sp_consensus_aura::SlotDuration; use sp_core::{crypto::KeyTypeId, ConstU128, ConstU16, OpaqueMetadata}; @@ -530,8 +530,8 @@ impl_runtime_apis! { // TODO: `keys` could and should be a BTreeSet, but it makes it more complicated for clients to build the type. So we use a Vec, since the keys are deduplicated anyway at proof creation time. // TODO: Support generating different versions of the proof, based on the provided parameter - impl kilt_runtime_api_dip_provider::DipProvider, Vec>, CompleteMerkleProof>, ()> for Runtime { - fn generate_proof(identifier: DidIdentifier, keys: Vec>) -> Result>, ()> { + impl kilt_runtime_api_dip_provider::DipProvider, Vec>, CompleteMerkleProof>, ()> for Runtime { + fn generate_proof(identifier: DidIdentifier, keys: Vec>) -> Result>, ()> { if let Ok(Some((did_details, _))) = ::IdentityProvider::retrieve(&identifier) { DidMerkleRootGenerator::::generate_proof(&did_details, keys.iter()) } else { diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index 36e5661d2d..f4d0b6379c 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -36,7 +36,7 @@ pub mod pallet { use parity_scale_codec::MaxEncodedLen; use sp_std::boxed::Box; - use dip_support::{latest::IdentityProofAction, VersionedIdentityProofAction}; + use dip_support::latest::IdentityProofAction; use crate::{proof::ProofEntry, traits::IdentityProofVerifier}; @@ -116,22 +116,21 @@ pub mod pallet { #[pallet::weight(0)] pub fn process_identity_action( origin: OriginFor, - action: VersionedIdentityProofAction, + action: IdentityProofAction, ) -> DispatchResult { ensure_sibling_para(::RuntimeOrigin::from(origin))?; let event = match action { - VersionedIdentityProofAction::V1(IdentityProofAction::Updated(identifier, proof, _)) => { + IdentityProofAction::Updated(identifier, proof, _) => { IdentityProofs::::mutate(&identifier, |entry| { *entry = Some(ProofEntry::from_digest(proof.clone())) }); Ok::<_, Error>(Event::::IdentityInfoUpdated(identifier, proof)) } - VersionedIdentityProofAction::V1(IdentityProofAction::Deleted(identifier)) => { + IdentityProofAction::Deleted(identifier) => { IdentityProofs::::remove(&identifier); Ok::<_, Error>(Event::::IdentityInfoDeleted(identifier)) } - _ => Err(Error::::UnsupportedVersion), }?; Self::deposit_event(event); diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index 4af958ccd8..acc9b93747 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -33,13 +33,11 @@ pub mod pallet { use sp_std::{boxed::Box, fmt::Debug}; use xcm::{latest::prelude::*, VersionedMultiAsset, VersionedMultiLocation}; - use dip_support::{v1::IdentityProofAction, VersionedIdentityProofAction}; + use dip_support::latest::IdentityProofAction; use crate::traits::{IdentityProofDispatcher, IdentityProofGenerator, IdentityProvider, TxBuilder}; pub type IdentityProofActionOf = IdentityProofAction<::Identifier, ::ProofOutput>; - pub type VersionedIdentityProofActionOf = - VersionedIdentityProofAction<::Identifier, ::ProofOutput>; const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); @@ -72,7 +70,7 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - IdentityInfoDispatched(VersionedIdentityProofActionOf, Box), + IdentityInfoDispatched(IdentityProofActionOf, Box), } #[pallet::error] @@ -110,23 +108,18 @@ pub mod pallet { Err(_) => Err(Error::::IdentityNotFound), }?; // TODO: Add correct version creation based on lookup (?) - let versioned_action = VersionedIdentityProofAction::V1(action); let asset: MultiAsset = (*asset).try_into().map_err(|_| Error::::BadVersion)?; - let (ticket, _) = T::IdentityProofDispatcher::pre_dispatch::( - versioned_action.clone(), - asset, - weight, - destination, - ) - .map_err(|_| Error::::Predispatch)?; + let (ticket, _) = + T::IdentityProofDispatcher::pre_dispatch::(action.clone(), asset, weight, destination) + .map_err(|_| Error::::Predispatch)?; // TODO: Use returned asset of `pre_dispatch` to charge the tx submitter for the // fee, in addition to the cost on the target chain. T::IdentityProofDispatcher::dispatch(ticket).map_err(|_| Error::::Dispatch)?; - Self::deposit_event(Event::IdentityInfoDispatched(versioned_action, Box::new(destination))); + Self::deposit_event(Event::IdentityInfoDispatched(action, Box::new(destination))); Ok(()) } } diff --git a/pallets/pallet-dip-provider/src/traits.rs b/pallets/pallet-dip-provider/src/traits.rs index a589fa3b1d..3b0e0c2342 100644 --- a/pallets/pallet-dip-provider/src/traits.rs +++ b/pallets/pallet-dip-provider/src/traits.rs @@ -16,7 +16,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use dip_support::VersionedIdentityProofAction; +use dip_support::latest::IdentityProofAction; use xcm::{latest::prelude::*, DoubleEncoded}; pub use identity_generation::*; @@ -61,7 +61,7 @@ pub mod identity_dispatch { type Error; fn pre_dispatch>( - action: VersionedIdentityProofAction, + action: IdentityProofAction, asset: MultiAsset, weight: Weight, destination: MultiLocation, @@ -80,7 +80,7 @@ pub mod identity_dispatch { type Error = (); fn pre_dispatch<_B>( - _action: VersionedIdentityProofAction, + _action: IdentityProofAction, _asset: MultiAsset, _weight: Weight, _destination: MultiLocation, @@ -111,7 +111,7 @@ pub mod identity_dispatch { type Error = SendError; fn pre_dispatch>( - action: VersionedIdentityProofAction, + action: IdentityProofAction, asset: MultiAsset, weight: Weight, destination: MultiLocation, @@ -190,6 +190,6 @@ pub trait TxBuilder { fn build( dest: MultiLocation, - action: VersionedIdentityProofAction, + action: IdentityProofAction, ) -> Result, Self::Error>; } diff --git a/runtimes/common/Cargo.toml b/runtimes/common/Cargo.toml index 6d6284722e..4b1e03c0a6 100644 --- a/runtimes/common/Cargo.toml +++ b/runtimes/common/Cargo.toml @@ -26,7 +26,7 @@ attestation.workspace = true ctype.workspace = true delegation = {workspace = true, optional = true} did.workspace = true -dip-support.workspace = true +kilt-dip-support.workspace = true pallet-did-lookup = {workspace = true, optional = true} pallet-dip-consumer.workspace = true pallet-dip-provider.workspace = true @@ -84,10 +84,10 @@ std = [ "ctype/std", "cumulus-primitives-core/std", "did/std", - "dip-support/std", "frame-support/std", "frame-system/std", "kilt-asset-dids/std", + "kilt-dip-support/std", "kilt-support/std", "log/std", "pallet-authorship/std", diff --git a/runtimes/common/src/dip/did.rs b/runtimes/common/src/dip/did.rs new file mode 100644 index 0000000000..e954f008e7 --- /dev/null +++ b/runtimes/common/src/dip/did.rs @@ -0,0 +1,42 @@ +// 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 did::did_details::DidDetails; +use pallet_dip_provider::traits::IdentityProvider; +use sp_std::marker::PhantomData; + +pub struct DidIdentityProvider(PhantomData); + +impl IdentityProvider, ()> for DidIdentityProvider +where + T: did::Config, +{ + // TODO: Proper error handling + type Error = (); + + fn retrieve(identifier: &T::DidIdentifier) -> Result, ())>, Self::Error> { + match ( + did::Pallet::::get_did(identifier), + did::Pallet::::get_deleted_did(identifier), + ) { + (Some(details), _) => Ok(Some((details, ()))), + (_, Some(_)) => Ok(None), + _ => Err(()), + } + } +} diff --git a/runtimes/common/src/dip/provider.rs b/runtimes/common/src/dip/merkle.rs similarity index 87% rename from runtimes/common/src/dip/provider.rs rename to runtimes/common/src/dip/merkle.rs index 6edf18135a..f92cc96805 100644 --- a/runtimes/common/src/dip/provider.rs +++ b/runtimes/common/src/dip/merkle.rs @@ -17,19 +17,20 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use did::{did_details::DidDetails, DidVerificationKeyRelationship, KeyIdOf}; -use dip_support::latest::MerkleProof; use frame_support::RuntimeDebug; -use pallet_dip_provider::traits::{IdentityProofGenerator, IdentityProvider}; +use kilt_dip_support::merkle::MerkleProof; +use pallet_dip_provider::traits::IdentityProofGenerator; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_std::{borrow::ToOwned, collections::btree_set::BTreeSet, marker::PhantomData, vec::Vec}; use sp_trie::{generate_trie_proof, LayoutV1, MemoryDB, TrieDBMutBuilder, TrieHash, TrieMut}; -use crate::dip::{KeyDetailsKey, KeyDetailsValue, KeyReferenceKey, KeyReferenceValue, KeyRelationship, ProofLeaf}; +use kilt_dip_support::merkle::{ + DidKeyRelationship, KeyDetailsKey, KeyDetailsValue, KeyReferenceKey, KeyReferenceValue, ProofLeaf, +}; pub type BlindedValue = Vec; - -pub type DidMerkleProof = +pub type DidMerkleProofOf = MerkleProof, ProofLeaf, ::BlockNumber>>; #[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] @@ -96,7 +97,7 @@ where .iter() .try_for_each(|id| -> Result<(), ()> { let enc_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( - KeyReferenceKey(*id, KeyRelationship::Encryption), + KeyReferenceKey(*id, DidKeyRelationship::Encryption), KeyReferenceValue, ); trie_builder @@ -128,7 +129,7 @@ where pub fn generate_proof<'a, K>( identity: &DidDetails, mut key_ids: K, - ) -> Result>, ()> + ) -> Result>, ()> where K: Iterator>, { @@ -160,7 +161,7 @@ where } if identity.key_agreement_keys.contains(key_id) { set.insert(ProofLeaf::KeyReference( - KeyReferenceKey(*key_id, KeyRelationship::Encryption), + KeyReferenceKey(*key_id, DidKeyRelationship::Encryption), KeyReferenceValue, )); }; @@ -178,7 +179,7 @@ where let proof = generate_trie_proof::, _, _, _>(&db, root, &encoded_keys).map_err(|_| ())?; Ok(CompleteMerkleProof { root, - proof: DidMerkleProof:: { + proof: DidMerkleProofOf:: { blinded: proof, revealed: leaves.into_iter().collect::>(), }, @@ -199,24 +200,3 @@ where Self::calculate_root_with_db(identity, &mut db) } } - -pub struct DidIdentityProvider(PhantomData); - -impl IdentityProvider, ()> for DidIdentityProvider -where - T: did::Config, -{ - // TODO: Proper error handling - type Error = (); - - fn retrieve(identifier: &T::DidIdentifier) -> Result, ())>, Self::Error> { - match ( - did::Pallet::::get_did(identifier), - did::Pallet::::get_deleted_did(identifier), - ) { - (Some(details), _) => Ok(Some((details, ()))), - (_, Some(_)) => Ok(None), - _ => Err(()), - } - } -} diff --git a/runtimes/common/src/dip/mod.rs b/runtimes/common/src/dip/mod.rs index 394fa06396..a3955297d6 100644 --- a/runtimes/common/src/dip/mod.rs +++ b/runtimes/common/src/dip/mod.rs @@ -16,77 +16,5 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::{did_details::DidPublicKeyDetails, DidVerificationKeyRelationship}; -use frame_support::RuntimeDebug; -use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; -use sp_std::vec::Vec; - -pub mod consumer; -pub mod provider; - -#[derive(Clone, Copy, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord, MaxEncodedLen)] -pub enum KeyRelationship { - Encryption, - Verification(DidVerificationKeyRelationship), -} - -impl From for KeyRelationship { - fn from(value: DidVerificationKeyRelationship) -> Self { - Self::Verification(value) - } -} - -impl TryFrom for DidVerificationKeyRelationship { - // TODO: Error handling - type Error = (); - - fn try_from(value: KeyRelationship) -> Result { - if let KeyRelationship::Verification(rel) = value { - Ok(rel) - } else { - Err(()) - } - } -} - -#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct KeyReferenceKey(pub KeyId, pub KeyRelationship); -#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct KeyReferenceValue; - -#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct KeyDetailsKey(pub KeyId); -#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct KeyDetailsValue(pub DidPublicKeyDetails); - -#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub enum ProofLeaf { - // 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. - KeyReference(KeyReferenceKey, KeyReferenceValue), - // The key and value for the leaves of a merkle proof that contain the actual - // details of a DID public key. The key is the ID of the key, and the value is its details, including creation - // block number. - KeyDetails(KeyDetailsKey, KeyDetailsValue), -} - -impl ProofLeaf -where - KeyId: Encode, - BlockNumber: Encode, -{ - pub(crate) fn encoded_key(&self) -> Vec { - match self { - ProofLeaf::KeyReference(key, _) => key.encode(), - ProofLeaf::KeyDetails(key, _) => key.encode(), - } - } - - pub(crate) fn encoded_value(&self) -> Vec { - match self { - ProofLeaf::KeyReference(_, value) => value.encode(), - ProofLeaf::KeyDetails(_, value) => value.encode(), - } - } -} +pub mod did; +pub mod merkle; From 785d4bb9b095469dbb35bf4bf460e058dc0b0f89 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 10 May 2023 09:30:05 +0200 Subject: [PATCH 14/26] Compiling and tests passing --- Cargo.lock | 2 +- crates/kilt-dip-support/src/did.rs | 62 +++++++++++++------ crates/kilt-dip-support/src/merkle.rs | 49 +++++++++------ dip-template/runtimes/dip-consumer/src/dip.rs | 24 +++++-- dip-template/runtimes/xcm-tests/Cargo.toml | 2 +- dip-template/runtimes/xcm-tests/src/tests.rs | 7 +-- .../src/{proof.rs => identity.rs} | 6 +- pallets/pallet-dip-consumer/src/lib.rs | 10 +-- pallets/pallet-dip-consumer/src/traits.rs | 8 +-- 9 files changed, 108 insertions(+), 62 deletions(-) rename pallets/pallet-dip-consumer/src/{proof.rs => identity.rs} (89%) diff --git a/Cargo.lock b/Cargo.lock index 8858a9c17c..3640aabd5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2436,9 +2436,9 @@ dependencies = [ "did", "dip-consumer-runtime-template", "dip-provider-runtime-template", - "dip-support", "frame-support", "frame-system", + "kilt-dip-support", "kilt-support", "pallet-balances", "pallet-did-lookup", diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index 8db87b2c4a..89c17fa0a4 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -20,36 +20,60 @@ use did::{ did_details::{DidPublicKey, DidPublicKeyDetails, DidVerificationKey}, DidSignature, DidVerificationKeyRelationship, }; -use pallet_dip_consumer::traits::IdentityProofVerifier; +use pallet_dip_consumer::{identity::IdentityDetails, traits::IdentityProofVerifier}; use parity_scale_codec::Encode; -use sp_core::{ConstU32, Get}; +use sp_core::Get; use sp_std::marker::PhantomData; use crate::{ - merkle::{ProofEntry, VerificationResult}, + merkle::ProofEntry, traits::{Bump, DidDipOriginFilter}, }; -pub struct DidSignatureVerifier( - PhantomData<(BlockNumber, Digest, Details, AccountId, SignedExtraProvider, S, L)>, +pub struct DidFromMerkleLeavesSignatureVerifier< + BlockNumber, + Digest, + Details, + AccountId, + SignedExtraProvider, + SignedExtra, + ProofEntries, +>( + PhantomData<( + BlockNumber, + Digest, + Details, + AccountId, + SignedExtraProvider, + SignedExtra, + ProofEntries, + )>, ); -impl +impl IdentityProofVerifier - for DidSignatureVerifier> -where + for DidFromMerkleLeavesSignatureVerifier< + BlockNumber, + Digest, + Details, + AccountId, + SignedExtraProvider, + SignedExtra, + ProofEntries, + > where AccountId: Encode, BlockNumber: Encode, Call: Encode, Digest: Encode, Details: Bump + Encode, - SignedExtraProvider: Get, - S: Encode, + SignedExtraProvider: Get, + SignedExtra: Encode, + ProofEntries: AsRef<[ProofEntry]>, { // TODO: Error handling type Error = (); - type Proof = (VerificationResult, DidSignature); - type ProofEntry = pallet_dip_consumer::proof::ProofEntry; + type Proof = (ProofEntries, DidSignature); + type IdentityDetails = IdentityDetails; type Submitter = AccountId; type VerificationResult = (DidVerificationKey, DidVerificationKeyRelationship); @@ -57,11 +81,11 @@ where call: &Call, _subject: &Subject, submitter: &Self::Submitter, - proof_entry: &mut Self::ProofEntry, + proof_entry: &mut Self::IdentityDetails, proof: &Self::Proof, ) -> Result { let encoded_payload = (call, proof_entry.details(), submitter, SignedExtraProvider::get()).encode(); - let mut proof_verification_keys = proof.0 .0.iter().filter_map( + let mut proof_verification_keys = proof.0.as_ref().iter().filter_map( |ProofEntry { key: DidPublicKeyDetails { key, .. }, relationship, @@ -105,7 +129,7 @@ where // FIXME: Better error handling type Error = (); type Proof = >::Proof; - type ProofEntry = >::ProofEntry; + type IdentityDetails = >::IdentityDetails; type Submitter = >::Submitter; type VerificationResult = >::VerificationResult; @@ -113,7 +137,7 @@ where call: &Call, subject: &Subject, submitter: &Self::Submitter, - proof_entry: &mut Self::ProofEntry, + proof_entry: &mut Self::IdentityDetails, proof: &Self::Proof, ) -> Result { let did_signing_key = @@ -141,7 +165,7 @@ where >::VerificationResult, DidSignature, ), - ProofEntry = >::ProofEntry, + IdentityDetails = >::IdentityDetails, Submitter = >::Submitter, >, { @@ -152,7 +176,7 @@ where >::Proof, DidSignature, ); - type ProofEntry = >::ProofEntry; + type IdentityDetails = >::IdentityDetails; type Submitter = >::Submitter; type VerificationResult = >::VerificationResult; @@ -160,7 +184,7 @@ where call: &Call, subject: &Subject, submitter: &Self::Submitter, - proof_entry: &mut Self::ProofEntry, + proof_entry: &mut Self::IdentityDetails, proof: &Self::Proof, ) -> Result { let merkle_proof_verification = diff --git a/crates/kilt-dip-support/src/merkle.rs b/crates/kilt-dip-support/src/merkle.rs index 9b4408094b..2746039a57 100644 --- a/crates/kilt-dip-support/src/merkle.rs +++ b/crates/kilt-dip-support/src/merkle.rs @@ -18,7 +18,7 @@ use did::{did_details::DidPublicKeyDetails, DidVerificationKeyRelationship}; use frame_support::{traits::ConstU32, RuntimeDebug}; -use pallet_dip_consumer::traits::IdentityProofVerifier; +use pallet_dip_consumer::{identity::IdentityDetails, traits::IdentityProofVerifier}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_runtime::BoundedVec; @@ -125,27 +125,38 @@ where // Contains the list of revealed public keys after a given merkle proof has been // correctly verified. -#[derive(Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen, Encode, Decode)] -#[cfg_attr(feature = "runtime-benchmarks", derive(Default))] -pub struct VerificationResult(pub BoundedVec, ConstU32>); +#[derive(Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen, Encode, Decode, Default)] +pub struct VerificationResult( + pub BoundedVec, ConstU32>, +); -impl From>> for VerificationResult -where - // TODO: Remove - // this bound - BlockNumber: Debug, +impl TryFrom>> + for VerificationResult +{ + // TODO: Better error handling + type Error = (); + + fn try_from(value: Vec>) -> Result { + let bounded_inner = value.try_into().map_err(|_| ())?; + Ok(Self(bounded_inner)) + } +} + +impl AsRef<[ProofEntry]> + for VerificationResult { - fn from(value: Vec>) -> Self { - Self(value.try_into().expect("Failed to put Vec into BoundedVec")) + fn as_ref(&self) -> &[ProofEntry] { + self.0.as_ref() } } -pub struct DidMerkleProofVerifier( - PhantomData<(Hasher, AccountId, KeyId, BlockNumber, Details, ConstU32)>, +pub struct DidMerkleProofVerifier( + PhantomData<(Hasher, AccountId, KeyId, BlockNumber, Details, MaxRevealedLeavesCount)>, ); -impl IdentityProofVerifier - for DidMerkleProofVerifier +impl + IdentityProofVerifier + for DidMerkleProofVerifier> where // TODO: Remove `Debug` bound BlockNumber: Encode + Clone + Debug, @@ -155,15 +166,15 @@ where // TODO: Proper error handling type Error = (); type Proof = MerkleProof>, ProofLeaf>; - type ProofEntry = pallet_dip_consumer::proof::ProofEntry; + type IdentityDetails = IdentityDetails; type Submitter = AccountId; - type VerificationResult = VerificationResult; + type VerificationResult = VerificationResult; fn verify_proof_for_call_against_entry( _call: &Call, _subject: &Subject, _submitter: &Self::Submitter, - proof_entry: &mut Self::ProofEntry, + proof_entry: &mut Self::IdentityDetails, proof: &Self::Proof, ) -> Result { // TODO: more efficient by removing cloning and/or collecting. @@ -219,6 +230,6 @@ where } }) .collect(); - Ok(keys.into()) + keys.try_into() } } diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index 3ad06e09d3..4fd2b5f600 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -19,10 +19,11 @@ use did::{did_details::DidVerificationKey, DidSignature, DidVerificationKeyRelationship, KeyIdOf}; use frame_support::traits::{Contains, Get}; use kilt_dip_support::{ - did::{DidSignatureAndCallVerifier, DidSignatureVerifier, MerkleProofAndDidSignatureVerifier}, + did::{DidFromMerkleLeavesSignatureVerifier, DidSignatureAndCallVerifier, MerkleProofAndDidSignatureVerifier}, merkle::{DidMerkleProofVerifier, MerkleProof, ProofLeaf}, traits::DidDipOriginFilter, }; +use pallet_dip_consumer::traits::IdentityProofVerifier; use sp_core::ConstU32; use sp_runtime::traits::Zero; use sp_std::vec::Vec; @@ -37,6 +38,20 @@ impl Get for GenesisProvider { } } +pub type MerkleProofVerifier = + DidMerkleProofVerifier, BlockNumber, u128, ConstU32<10>>; +pub type MerkleProofVerifierOutputOf = + >::VerificationResult; +pub type MerkleDidSignatureVerifierOf = DidFromMerkleLeavesSignatureVerifier< + BlockNumber, + Hash, + u128, + AccountId, + GenesisProvider, + Hash, + MerkleProofVerifierOutputOf, +>; + impl pallet_dip_consumer::Config for Runtime { type DipCallOriginFilter = PreliminaryDipOriginFilter; type Identifier = DidIdentifier; @@ -44,11 +59,8 @@ impl pallet_dip_consumer::Config for Runtime { type Proof = (MerkleProof>, ProofLeaf>, DidSignature); type ProofDigest = Hash; type ProofVerifier = MerkleProofAndDidSignatureVerifier< - DidMerkleProofVerifier, BlockNumber, u128, 10>, - DidSignatureAndCallVerifier< - DidSignatureVerifier>, - DipCallFilter, - >, + MerkleProofVerifier, + DidSignatureAndCallVerifier, DipCallFilter>, >; type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; diff --git a/dip-template/runtimes/xcm-tests/Cargo.toml b/dip-template/runtimes/xcm-tests/Cargo.toml index 91e6ae3fbb..6be31a5cb1 100644 --- a/dip-template/runtimes/xcm-tests/Cargo.toml +++ b/dip-template/runtimes/xcm-tests/Cargo.toml @@ -15,9 +15,9 @@ cumulus-pallet-xcmp-queue = { workspace = true, features = ["std"] } did = { workspace = true, features = ["std"] } dip-consumer-runtime-template = { workspace = true, features = ["std"] } dip-provider-runtime-template = { workspace = true, features = ["std"] } -dip-support = { workspace = true, features = ["std"] } frame-support = { workspace = true, features = ["std"] } frame-system = { workspace = true, features = ["std"] } +kilt-dip-support = { workspace = true, features = ["std"] } kilt-support = { workspace = true, features = ["std"] } pallet-balances = { workspace = true, features = ["std"] } pallet-did-lookup = { workspace = true, features = ["std"] } diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index 4211150204..661ee4ff55 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -19,12 +19,12 @@ use super::*; use did::{Did, DidSignature}; -use dip_support::latest::MerkleProof; use frame_support::{assert_ok, weights::Weight}; use frame_system::RawOrigin; +use kilt_dip_support::merkle::MerkleProof; use pallet_did_lookup::linkable_account::LinkableAccountId; use parity_scale_codec::Encode; -use runtime_common::dip::provider::{CompleteMerkleProof, DidMerkleRootGenerator}; +use runtime_common::dip::merkle::{CompleteMerkleProof, DidMerkleRootGenerator}; use sp_core::Pair; use sp_runtime::traits::Zero; use xcm::latest::{ @@ -100,8 +100,7 @@ fn commit_identity() { MerkleProof { blinded: proof.blinded, revealed: proof.revealed, - } - .into(), + }, signature ), Box::new(call), diff --git a/pallets/pallet-dip-consumer/src/proof.rs b/pallets/pallet-dip-consumer/src/identity.rs similarity index 89% rename from pallets/pallet-dip-consumer/src/proof.rs rename to pallets/pallet-dip-consumer/src/identity.rs index 3c9e22700c..02666a8827 100644 --- a/pallets/pallet-dip-consumer/src/proof.rs +++ b/pallets/pallet-dip-consumer/src/identity.rs @@ -21,12 +21,12 @@ use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; #[derive(Encode, Decode, MaxEncodedLen, Default, TypeInfo, RuntimeDebug)] -pub struct ProofEntry { +pub struct IdentityDetails { pub digest: Digest, pub details: Details, } -impl ProofEntry { +impl IdentityDetails { pub fn digest(&self) -> &Digest { &self.digest } @@ -35,7 +35,7 @@ impl ProofEntry { } } -impl ProofEntry +impl IdentityDetails where Details: Default, { diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index f4d0b6379c..9c463d2971 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -20,8 +20,8 @@ #![cfg_attr(not(feature = "std"), no_std)] +pub mod identity; mod origin; -pub mod proof; pub mod traits; pub use crate::{origin::*, pallet::*}; @@ -38,7 +38,7 @@ pub mod pallet { use dip_support::latest::IdentityProofAction; - use crate::{proof::ProofEntry, traits::IdentityProofVerifier}; + use crate::{identity::IdentityDetails, traits::IdentityProofVerifier}; pub type VerificationResultOf = <::ProofVerifier as IdentityProofVerifier< ::RuntimeCall, @@ -54,7 +54,7 @@ pub mod pallet { _, Twox64Concat, ::Identifier, - ProofEntry<::ProofDigest, ::IdentityDetails>, + IdentityDetails<::ProofDigest, ::IdentityDetails>, >; #[pallet::config] @@ -68,7 +68,7 @@ pub mod pallet { ::RuntimeCall, Self::Identifier, Proof = Self::Proof, - ProofEntry = ProofEntry, + IdentityDetails = IdentityDetails, Submitter = ::AccountId, >; type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin>; @@ -123,7 +123,7 @@ pub mod pallet { let event = match action { IdentityProofAction::Updated(identifier, proof, _) => { IdentityProofs::::mutate(&identifier, |entry| { - *entry = Some(ProofEntry::from_digest(proof.clone())) + *entry = Some(IdentityDetails::from_digest(proof.clone())) }); Ok::<_, Error>(Event::::IdentityInfoUpdated(identifier, proof)) } diff --git a/pallets/pallet-dip-consumer/src/traits.rs b/pallets/pallet-dip-consumer/src/traits.rs index f437672b1a..7f4645f166 100644 --- a/pallets/pallet-dip-consumer/src/traits.rs +++ b/pallets/pallet-dip-consumer/src/traits.rs @@ -21,7 +21,7 @@ use sp_std::marker::PhantomData; pub trait IdentityProofVerifier { type Error; type Proof; - type ProofEntry; + type IdentityDetails; type Submitter; type VerificationResult; @@ -29,7 +29,7 @@ pub trait IdentityProofVerifier { call: &Call, subject: &Subject, submitter: &Self::Submitter, - proof_entry: &mut Self::ProofEntry, + proof_entry: &mut Self::IdentityDetails, proof: &Self::Proof, ) -> Result; } @@ -41,7 +41,7 @@ impl IdentityProofVerifier IdentityProofVerifier Result { Ok(()) From 9fbbe0a7310a185d60e27b42c85569c7a116c4a3 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 10 May 2023 10:17:43 +0200 Subject: [PATCH 15/26] More comments --- crates/kilt-dip-support/src/did.rs | 54 +++++++++++++++---- crates/kilt-dip-support/src/merkle.rs | 7 ++- crates/kilt-dip-support/src/traits.rs | 10 ++++ dip-template/runtimes/dip-consumer/src/dip.rs | 4 +- pallets/pallet-dip-consumer/src/identity.rs | 4 ++ pallets/pallet-dip-consumer/src/lib.rs | 42 ++++++++++++--- 6 files changed, 100 insertions(+), 21 deletions(-) diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index 89c17fa0a4..66c543790d 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -30,14 +30,20 @@ use crate::{ traits::{Bump, DidDipOriginFilter}, }; -pub struct DidFromMerkleLeavesSignatureVerifier< +/// A type that verifies a DID signature over some DID keys revealed by a +/// previously-verified Merkle proof. It requires the `Details` type to +/// implement the `Bump` trait to avoid replay attacks. The basic verification +/// logic verifies that the signature has been generated over the encoded tuple +/// (call, identity details). Additional details can be added to the end of the +/// tuple by providing a `SignedExtraProvider`. +pub struct MerkleRevealedDidSignatureVerifier< BlockNumber, Digest, Details, AccountId, SignedExtraProvider, SignedExtra, - ProofEntries, + MerkleProofEntries, >( PhantomData<( BlockNumber, @@ -46,20 +52,20 @@ pub struct DidFromMerkleLeavesSignatureVerifier< AccountId, SignedExtraProvider, SignedExtra, - ProofEntries, + MerkleProofEntries, )>, ); -impl +impl IdentityProofVerifier - for DidFromMerkleLeavesSignatureVerifier< + for MerkleRevealedDidSignatureVerifier< BlockNumber, Digest, Details, AccountId, SignedExtraProvider, SignedExtra, - ProofEntries, + MerkleProofEntries, > where AccountId: Encode, BlockNumber: Encode, @@ -68,13 +74,20 @@ impl, SignedExtra: Encode, - ProofEntries: AsRef<[ProofEntry]>, + MerkleProofEntries: AsRef<[ProofEntry]>, { // TODO: Error handling type Error = (); - type Proof = (ProofEntries, DidSignature); + /// The proof must be a list of Merkle leaves that have been previously + /// verified by a different verifier. + type Proof = (MerkleProofEntries, DidSignature); + /// The `Details` that are part of the identity details must implement the + /// `Bump` trait. type IdentityDetails = IdentityDetails; + /// The type of the submitter's accounts. type Submitter = AccountId; + /// Successful verifications return the verification key used to validate + /// the provided signature and its relationship to the DID subject. type VerificationResult = (DidVerificationKey, DidVerificationKeyRelationship); fn verify_proof_for_call_against_entry( @@ -85,6 +98,7 @@ impl Result { let encoded_payload = (call, proof_entry.details(), submitter, SignedExtraProvider::get()).encode(); + // Only consider verification keys from the set of revealed merkle leaves. let mut proof_verification_keys = proof.0.as_ref().iter().filter_map( |ProofEntry { key: DidPublicKeyDetails { key, .. }, @@ -111,8 +125,14 @@ impl( PhantomData<(DidSignatureVerifier, CallVerifier)>, ); @@ -128,9 +148,16 @@ where { // FIXME: Better error handling type Error = (); + /// The input proof is the same accepted by the `DidSignatureVerifier`. type Proof = >::Proof; + /// The identity details are the same accepted by the + /// `DidSignatureVerifier`. type IdentityDetails = >::IdentityDetails; + /// The submitter address is the same accepted by the + /// `DidSignatureVerifier`. type Submitter = >::Submitter; + /// The verification result is the same accepted by the + /// `DidSignatureVerifier`. type VerificationResult = >::VerificationResult; fn verify_proof_for_call_against_entry( @@ -148,6 +175,13 @@ where } } +/// A type that chains a Merkle proof verification with a DID signature +/// verification. The required input of this type is a tuple (A, B) where A is +/// the type of input required by the `MerkleProofVerifier` and B is a +/// `DidSignature. +/// The successful output of this type is the output type of the +/// `MerkleProofVerifier`, meaning that DID signature verification happens +/// internally and does not transform the result in any way. pub struct MerkleProofAndDidSignatureVerifier( PhantomData<(MerkleProofVerifier, DidSignatureVerifier)>, ); diff --git a/crates/kilt-dip-support/src/merkle.rs b/crates/kilt-dip-support/src/merkle.rs index 2746039a57..7ee449dd54 100644 --- a/crates/kilt-dip-support/src/merkle.rs +++ b/crates/kilt-dip-support/src/merkle.rs @@ -16,7 +16,10 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::{did_details::DidPublicKeyDetails, DidVerificationKeyRelationship}; +use did::{ + did_details::{DidEncryptionKey, DidPublicKey, DidPublicKeyDetails}, + DidVerificationKeyRelationship, +}; use frame_support::{traits::ConstU32, RuntimeDebug}; use pallet_dip_consumer::{identity::IdentityDetails, traits::IdentityProofVerifier}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; @@ -150,6 +153,8 @@ impl AsRef<[ProofEntry( PhantomData<(Hasher, AccountId, KeyId, BlockNumber, Details, MaxRevealedLeavesCount)>, ); diff --git a/crates/kilt-dip-support/src/traits.rs b/crates/kilt-dip-support/src/traits.rs index 45dc099876..be71e2b721 100644 --- a/crates/kilt-dip-support/src/traits.rs +++ b/crates/kilt-dip-support/src/traits.rs @@ -18,7 +18,11 @@ use sp_runtime::traits::{CheckedAdd, One, Zero}; +/// A trait for "bumpable" types, i.e., types that have some notion of order of +/// its members. pub trait Bump { + /// Bump the type instance to its next value. Overflows are assumed to be + /// taken care of by the type internal logic. fn bump(&mut self); } @@ -36,9 +40,15 @@ where } } +/// A trait for types that implement some sort of access control logic on the +/// provided input `Call` type. pub trait DidDipOriginFilter { + /// The error type for cases where the checks fail. type Error; + /// The type of additional information required by the type to perform the + /// checks on the `Call` input. type OriginInfo; + /// The success type for cases where the checks succeed. type Success; fn check_call_origin_info(call: &Call, info: &Self::OriginInfo) -> Result; diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index 4fd2b5f600..5497df62c0 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -19,7 +19,7 @@ use did::{did_details::DidVerificationKey, DidSignature, DidVerificationKeyRelationship, KeyIdOf}; use frame_support::traits::{Contains, Get}; use kilt_dip_support::{ - did::{DidFromMerkleLeavesSignatureVerifier, DidSignatureAndCallVerifier, MerkleProofAndDidSignatureVerifier}, + did::{DidSignatureAndCallVerifier, MerkleProofAndDidSignatureVerifier, MerkleRevealedDidSignatureVerifier}, merkle::{DidMerkleProofVerifier, MerkleProof, ProofLeaf}, traits::DidDipOriginFilter, }; @@ -42,7 +42,7 @@ pub type MerkleProofVerifier = DidMerkleProofVerifier, BlockNumber, u128, ConstU32<10>>; pub type MerkleProofVerifierOutputOf = >::VerificationResult; -pub type MerkleDidSignatureVerifierOf = DidFromMerkleLeavesSignatureVerifier< +pub type MerkleDidSignatureVerifierOf = MerkleRevealedDidSignatureVerifier< BlockNumber, Hash, u128, diff --git a/pallets/pallet-dip-consumer/src/identity.rs b/pallets/pallet-dip-consumer/src/identity.rs index 02666a8827..e4b8350b31 100644 --- a/pallets/pallet-dip-consumer/src/identity.rs +++ b/pallets/pallet-dip-consumer/src/identity.rs @@ -20,9 +20,13 @@ 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, } diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index 9c463d2971..12ebe4931d 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -59,11 +59,24 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { + /// Preliminary filter to filter out calls before doing any heavier + /// computations. type DipCallOriginFilter: Contains<::RuntimeCall>; + /// The identifier of a subject, e.g., a DID. type Identifier: Parameter + MaxEncodedLen; + /// The details stored in this pallet associated with any given subject. type IdentityDetails: Parameter + MaxEncodedLen + Default; + /// The proof users must provide to operate with their higher-level + /// identity. Depending on the use cases, this proof can contain + /// heterogeneous bits of information that the proof verifier will + /// utilize. For instance, a proof could contain both a Merkle proof and + /// a DID signature. type Proof: Parameter; + /// The type of the committed proof digest used as the basis for + /// verifying identity proofs. type ProofDigest: Parameter + MaxEncodedLen; + /// The logic of the proof verifier, called upon each execution of the + /// `dispatch_as` extrinsic. type ProofVerifier: IdentityProofVerifier< ::RuntimeCall, Self::Identifier, @@ -71,8 +84,11 @@ pub mod pallet { IdentityDetails = IdentityDetails, Submitter = ::AccountId, >; + /// The overarching runtime call type. type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin>; + /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// The overarching runtime origin type. type RuntimeOrigin: From> + From<::RuntimeOrigin> + Into::RuntimeOrigin>>; @@ -86,19 +102,26 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { + /// The identity information related to a given subject has been + /// deleted. IdentityInfoDeleted(T::Identifier), + /// The identity information related to a given subject has been updated + /// to a new digest. IdentityInfoUpdated(T::Identifier, T::ProofDigest), } #[pallet::error] pub enum Error { - Dispatch, + /// An identity with the provided identifier could not be found. IdentityNotFound, + /// The identity proof provided could not be successfully verified. InvalidProof, - UnsupportedVersion, + /// The specified call could not be dispatched. + Dispatch, } - // The new origin other pallets can use. + /// The origin this pallet creates after a user has provided a valid + /// identity proof to dispatch other calls. #[pallet::origin] pub type Origin = DipOrigin< ::Identifier, @@ -122,9 +145,12 @@ pub mod pallet { let event = match action { IdentityProofAction::Updated(identifier, proof, _) => { - IdentityProofs::::mutate(&identifier, |entry| { - *entry = Some(IdentityDetails::from_digest(proof.clone())) - }); + IdentityProofs::::mutate( + &identifier, + |entry: &mut Option< + IdentityDetails<::ProofDigest, ::IdentityDetails>, + >| { *entry = Some(IdentityDetails::from_digest(proof.clone())) }, + ); Ok::<_, Error>(Event::::IdentityInfoUpdated(identifier, proof)) } IdentityProofAction::Deleted(identifier) => { @@ -159,8 +185,8 @@ pub mod pallet { &proof, ) .map_err(|_| Error::::InvalidProof)?; - // Update the identity info after it has optionally been updated by the - // `ProofVerifier`. + // Write the identity info to storage after it has optionally been updated by + // the `ProofVerifier`. IdentityProofs::::mutate(&identifier, |entry| *entry = Some(proof_entry)); let did_origin = DipOrigin { identifier, From ac704654253aff8265c68c928acbc331c71d1eb0 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 10 May 2023 10:51:56 +0200 Subject: [PATCH 16/26] More refactoring --- crates/kilt-dip-support/src/did.rs | 66 +-------------------------- crates/kilt-dip-support/src/lib.rs | 66 +++++++++++++++++++++++++++ crates/kilt-dip-support/src/merkle.rs | 14 +++--- 3 files changed, 76 insertions(+), 70 deletions(-) diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index 66c543790d..c5251cacb5 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -79,7 +79,7 @@ impl Result { let encoded_payload = (call, proof_entry.details(), submitter, SignedExtraProvider::get()).encode(); - // Only consider verification keys from the set of revealed merkle leaves. + // Only consider verification keys from the set of revealed Merkle leaves. let mut proof_verification_keys = proof.0.as_ref().iter().filter_map( |ProofEntry { key: DidPublicKeyDetails { key, .. }, @@ -174,65 +174,3 @@ where Ok(did_signing_key) } } - -/// A type that chains a Merkle proof verification with a DID signature -/// verification. The required input of this type is a tuple (A, B) where A is -/// the type of input required by the `MerkleProofVerifier` and B is a -/// `DidSignature. -/// The successful output of this type is the output type of the -/// `MerkleProofVerifier`, meaning that DID signature verification happens -/// internally and does not transform the result in any way. -pub struct MerkleProofAndDidSignatureVerifier( - PhantomData<(MerkleProofVerifier, DidSignatureVerifier)>, -); - -impl IdentityProofVerifier - for MerkleProofAndDidSignatureVerifier -where - MerkleProofVerifier: IdentityProofVerifier, - // TODO: get rid of this if possible - MerkleProofVerifier::VerificationResult: Clone, - DidSignatureVerifier: IdentityProofVerifier< - Call, - Subject, - Proof = ( - >::VerificationResult, - DidSignature, - ), - IdentityDetails = >::IdentityDetails, - Submitter = >::Submitter, - >, -{ - // FIXME: Better error handling - type Error = (); - // FIXME: Better type declaration - type Proof = ( - >::Proof, - DidSignature, - ); - type IdentityDetails = >::IdentityDetails; - type Submitter = >::Submitter; - type VerificationResult = >::VerificationResult; - - fn verify_proof_for_call_against_entry( - call: &Call, - subject: &Subject, - submitter: &Self::Submitter, - proof_entry: &mut Self::IdentityDetails, - proof: &Self::Proof, - ) -> Result { - let merkle_proof_verification = - MerkleProofVerifier::verify_proof_for_call_against_entry(call, subject, submitter, proof_entry, &proof.0) - .map_err(|_| ())?; - DidSignatureVerifier::verify_proof_for_call_against_entry( - call, - subject, - submitter, - proof_entry, - // FIXME: Remove `clone()` requirement - &(merkle_proof_verification.clone(), proof.1.clone()), - ) - .map_err(|_| ())?; - Ok(merkle_proof_verification) - } -} diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-support/src/lib.rs index 7e8d878e79..67c47e2636 100644 --- a/crates/kilt-dip-support/src/lib.rs +++ b/crates/kilt-dip-support/src/lib.rs @@ -20,6 +20,72 @@ #![cfg_attr(not(feature = "std"), no_std)] +use ::did::DidSignature; +use pallet_dip_consumer::traits::IdentityProofVerifier; +use sp_std::marker::PhantomData; + pub mod did; pub mod merkle; pub mod traits; + +/// A type that chains a Merkle proof verification with a DID signature +/// verification. The required input of this type is a tuple (A, B) where A is +/// the type of input required by the `MerkleProofVerifier` and B is a +/// `DidSignature. +/// The successful output of this type is the output type of the +/// `MerkleProofVerifier`, meaning that DID signature verification happens +/// internally and does not transform the result in any way. +pub struct MerkleProofAndDidSignatureVerifier( + PhantomData<(MerkleProofVerifier, DidSignatureVerifier)>, +); + +impl IdentityProofVerifier + for MerkleProofAndDidSignatureVerifier +where + MerkleProofVerifier: IdentityProofVerifier, + // TODO: get rid of this if possible + MerkleProofVerifier::VerificationResult: Clone, + DidSignatureVerifier: IdentityProofVerifier< + Call, + Subject, + Proof = ( + >::VerificationResult, + DidSignature, + ), + IdentityDetails = >::IdentityDetails, + Submitter = >::Submitter, + >, +{ + // FIXME: Better error handling + type Error = (); + // FIXME: Better type declaration + type Proof = ( + >::Proof, + DidSignature, + ); + type IdentityDetails = >::IdentityDetails; + type Submitter = >::Submitter; + type VerificationResult = >::VerificationResult; + + fn verify_proof_for_call_against_entry( + call: &Call, + subject: &Subject, + submitter: &Self::Submitter, + proof_entry: &mut Self::IdentityDetails, + proof: &Self::Proof, + ) -> Result { + let merkle_proof_verification = + MerkleProofVerifier::verify_proof_for_call_against_entry(call, subject, submitter, proof_entry, &proof.0) + .map_err(|_| ())?; + DidSignatureVerifier::verify_proof_for_call_against_entry( + call, + subject, + submitter, + proof_entry, + // FIXME: Remove `clone()` requirement + &(merkle_proof_verification.clone(), proof.1.clone()), + ) + .map_err(|_| ())?; + Ok(merkle_proof_verification) + } +} diff --git a/crates/kilt-dip-support/src/merkle.rs b/crates/kilt-dip-support/src/merkle.rs index 7ee449dd54..25aa753343 100644 --- a/crates/kilt-dip-support/src/merkle.rs +++ b/crates/kilt-dip-support/src/merkle.rs @@ -16,10 +16,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::{ - did_details::{DidEncryptionKey, DidPublicKey, DidPublicKeyDetails}, - DidVerificationKeyRelationship, -}; +use did::{did_details::DidPublicKeyDetails, DidVerificationKeyRelationship}; use frame_support::{traits::ConstU32, RuntimeDebug}; use pallet_dip_consumer::{identity::IdentityDetails, traits::IdentityProofVerifier}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; @@ -85,7 +82,6 @@ pub enum ProofLeaf { impl ProofLeaf where KeyId: Encode, - BlockNumber: Encode, { pub fn encoded_key(&self) -> Vec { match self { @@ -93,7 +89,12 @@ where ProofLeaf::KeyDetails(key, _) => key.encode(), } } +} +impl ProofLeaf +where + BlockNumber: Encode, +{ pub fn encoded_value(&self) -> Vec { match self { ProofLeaf::KeyReference(_, value) => value.encode(), @@ -126,7 +127,7 @@ where } } -// Contains the list of revealed public keys after a given merkle proof has been +// Contains the list of revealed public keys after a given Merkle proof has been // correctly verified. #[derive(Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen, Encode, Decode, Default)] pub struct VerificationResult( @@ -155,6 +156,7 @@ impl AsRef<[ProofEntry( PhantomData<(Hasher, AccountId, KeyId, BlockNumber, Details, MaxRevealedLeavesCount)>, ); From fc5009bdf6c5e313d93defd4aeba78a292957143 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 10 May 2023 13:39:57 +0200 Subject: [PATCH 17/26] Tests passing --- crates/kilt-dip-support/src/did.rs | 51 ++++++++++++++++--- crates/kilt-dip-support/src/lib.rs | 13 ++--- crates/kilt-dip-support/src/merkle.rs | 17 +++++-- dip-template/runtimes/dip-consumer/src/dip.rs | 24 +++++++-- dip-template/runtimes/xcm-tests/src/tests.rs | 8 ++- 5 files changed, 89 insertions(+), 24 deletions(-) diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index c5251cacb5..ac49baaf27 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -20,9 +20,11 @@ use did::{ did_details::{DidPublicKey, DidPublicKeyDetails, DidVerificationKey}, DidSignature, DidVerificationKeyRelationship, }; +use frame_support::ensure; use pallet_dip_consumer::{identity::IdentityDetails, traits::IdentityProofVerifier}; use parity_scale_codec::Encode; -use sp_core::Get; +use sp_core::{ConstU64, Get}; +use sp_runtime::traits::CheckedSub; use sp_std::marker::PhantomData; use crate::{ @@ -44,7 +46,10 @@ pub struct MerkleRevealedDidSignatureVerifier< SignedExtraProvider, SignedExtra, MerkleProofEntries, + BlockNumberProvider, + const SIGNATURE_VALIDITY: u64, >( + #[allow(clippy::type_complexity)] PhantomData<( BlockNumber, Digest, @@ -53,11 +58,24 @@ pub struct MerkleRevealedDidSignatureVerifier< SignedExtraProvider, SignedExtra, MerkleProofEntries, + BlockNumberProvider, + ConstU64, )>, ); -impl - IdentityProofVerifier +impl< + Call, + Subject, + BlockNumber, + Digest, + Details, + AccountId, + SignedExtraProvider, + SignedExtra, + MerkleProofEntries, + BlockNumberProvider, + const SIGNATURE_VALIDITY: u64, + > IdentityProofVerifier for MerkleRevealedDidSignatureVerifier< BlockNumber, Digest, @@ -66,21 +84,24 @@ impl where AccountId: Encode, - BlockNumber: Encode, + BlockNumber: Encode + CheckedSub + Into + PartialOrd + sp_std::fmt::Debug, Call: Encode, Digest: Encode, Details: Bump + Encode, SignedExtraProvider: Get, SignedExtra: Encode, MerkleProofEntries: AsRef<[ProofEntry]>, + BlockNumberProvider: Get, { // TODO: Error handling type Error = (); /// The proof must be a list of Merkle leaves that have been previously /// verified by the Merkle proof verifier, and the additional DID signature. - type Proof = (MerkleProofEntries, DidSignature); + type Proof = (MerkleProofEntries, (DidSignature, BlockNumber)); /// The `Details` that are part of the identity details must implement the /// `Bump` trait. type IdentityDetails = IdentityDetails; @@ -97,7 +118,23 @@ impl Result { - let encoded_payload = (call, proof_entry.details(), submitter, SignedExtraProvider::get()).encode(); + let block_number = BlockNumberProvider::get(); + let is_signature_fresh = if let Some(blocks_ago_from_now) = block_number.checked_sub(&proof.1 .1) { + // False if the signature is too old. + blocks_ago_from_now.into() <= SIGNATURE_VALIDITY + } else { + // Signature generated at a future time, not possible to verify. + false + }; + ensure!(is_signature_fresh, ()); + let encoded_payload = ( + call, + proof_entry.details(), + submitter, + block_number, + SignedExtraProvider::get(), + ) + .encode(); // Only consider verification keys from the set of revealed Merkle leaves. let mut proof_verification_keys = proof.0.as_ref().iter().filter_map( |ProofEntry { @@ -115,7 +152,7 @@ impl( - PhantomData<(MerkleProofVerifier, DidSignatureVerifier)>, +pub struct MerkleProofAndDidSignatureVerifier( + PhantomData<(BlockNumber, MerkleProofVerifier, DidSignatureVerifier)>, ); -impl IdentityProofVerifier - for MerkleProofAndDidSignatureVerifier +impl IdentityProofVerifier + for MerkleProofAndDidSignatureVerifier where + BlockNumber: Clone, MerkleProofVerifier: IdentityProofVerifier, // TODO: get rid of this if possible MerkleProofVerifier::VerificationResult: Clone, @@ -50,7 +51,7 @@ where Subject, Proof = ( >::VerificationResult, - DidSignature, + (DidSignature, BlockNumber), ), IdentityDetails = >::IdentityDetails, Submitter = >::Submitter, @@ -61,7 +62,7 @@ where // FIXME: Better type declaration type Proof = ( >::Proof, - DidSignature, + (DidSignature, BlockNumber), ); type IdentityDetails = >::IdentityDetails; type Submitter = >::Submitter; diff --git a/crates/kilt-dip-support/src/merkle.rs b/crates/kilt-dip-support/src/merkle.rs index 25aa753343..d86e84475a 100644 --- a/crates/kilt-dip-support/src/merkle.rs +++ b/crates/kilt-dip-support/src/merkle.rs @@ -119,7 +119,9 @@ where fn default() -> Self { Self { key: DidPublicKeyDetails { - key: DidPublicKey::PublicEncryptionKey(DidEncryptionKey::X25519([0u8; 32])), + key: did::did_details::DidPublicKey::PublicEncryptionKey(did::did_details::DidEncryptionKey::X25519( + [0u8; 32], + )), block_number: BlockNumber::default(), }, relationship: DidVerificationKeyRelationship::Authentication.into(), @@ -157,13 +159,20 @@ impl AsRef<[ProofEntry( - PhantomData<(Hasher, AccountId, KeyId, BlockNumber, Details, MaxRevealedLeavesCount)>, +pub struct DidMerkleProofVerifier( + PhantomData<( + Hasher, + AccountId, + KeyId, + BlockNumber, + Details, + ConstU32, + )>, ); impl IdentityProofVerifier - for DidMerkleProofVerifier> + for DidMerkleProofVerifier where // TODO: Remove `Debug` bound BlockNumber: Encode + Clone + Debug, diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index 5497df62c0..7ba705c0dd 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -19,12 +19,12 @@ use did::{did_details::DidVerificationKey, DidSignature, DidVerificationKeyRelationship, KeyIdOf}; use frame_support::traits::{Contains, Get}; use kilt_dip_support::{ - did::{DidSignatureAndCallVerifier, MerkleProofAndDidSignatureVerifier, MerkleRevealedDidSignatureVerifier}, + did::{DidSignatureAndCallVerifier, MerkleRevealedDidSignatureVerifier}, merkle::{DidMerkleProofVerifier, MerkleProof, ProofLeaf}, traits::DidDipOriginFilter, + MerkleProofAndDidSignatureVerifier, }; use pallet_dip_consumer::traits::IdentityProofVerifier; -use sp_core::ConstU32; use sp_runtime::traits::Zero; use sp_std::vec::Vec; @@ -38,8 +38,15 @@ impl Get for GenesisProvider { } } -pub type MerkleProofVerifier = - DidMerkleProofVerifier, BlockNumber, u128, ConstU32<10>>; +pub struct BlockNumberProvider; + +impl Get for BlockNumberProvider { + fn get() -> BlockNumber { + frame_system::Pallet::::block_number() + } +} + +pub type MerkleProofVerifier = DidMerkleProofVerifier, BlockNumber, u128, 10>; pub type MerkleProofVerifierOutputOf = >::VerificationResult; pub type MerkleDidSignatureVerifierOf = MerkleRevealedDidSignatureVerifier< @@ -50,15 +57,22 @@ pub type MerkleDidSignatureVerifierOf = MerkleRevealedDidSignatur GenesisProvider, Hash, MerkleProofVerifierOutputOf, + BlockNumberProvider, + // Signatures are valid for 50 blocks + 50, >; impl pallet_dip_consumer::Config for Runtime { type DipCallOriginFilter = PreliminaryDipOriginFilter; type Identifier = DidIdentifier; type IdentityDetails = u128; - type Proof = (MerkleProof>, ProofLeaf>, DidSignature); + type Proof = ( + MerkleProof>, ProofLeaf>, + (DidSignature, BlockNumber), + ); type ProofDigest = Hash; type ProofVerifier = MerkleProofAndDidSignatureVerifier< + BlockNumber, MerkleProofVerifier, DidSignatureAndCallVerifier, DipCallFilter>, >; diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index 661ee4ff55..625af89166 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -83,11 +83,15 @@ fn commit_identity() { ) .expect("Proof generation should not fail"); // 3.2 Generate a DID signature + let genesis_hash = + ConsumerParachain::execute_with(|| frame_system::Pallet::::block_hash(BlockNumber::zero())); + let system_block = ConsumerParachain::execute_with(|| frame_system::Pallet::::block_number()); let payload = ( call.clone(), 0u128, para::consumer::DISPATCHER_ACCOUNT, - ConsumerParachain::execute_with(|| frame_system::Pallet::::block_hash(BlockNumber::zero())), + system_block, + genesis_hash, ); let signature: DidSignature = para::provider::did_auth_key().sign(&payload.encode()).into(); // 3.3 Call the `dispatch_as` extrinsic on the consumer chain with the generated @@ -101,7 +105,7 @@ fn commit_identity() { blinded: proof.blinded, revealed: proof.revealed, }, - signature + (signature, system_block) ), Box::new(call), )); From 2700ec1536512b616c576530b2b5dd0652d57f8c Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 10 May 2023 14:27:17 +0200 Subject: [PATCH 18/26] Ultimate refactoring --- Cargo.lock | 1 + crates/dip-support/src/lib.rs | 12 ++++++--- crates/dip-support/src/v1.rs | 27 ------------------- crates/kilt-dip-support/Cargo.toml | 2 ++ crates/kilt-dip-support/src/traits.rs | 25 +++++++++++++++++ dip-template/runtimes/dip-consumer/src/dip.rs | 25 +++-------------- dip-template/runtimes/dip-provider/src/dip.rs | 2 +- dip-template/runtimes/xcm-tests/src/tests.rs | 2 +- pallets/pallet-dip-consumer/src/lib.rs | 15 ++++------- pallets/pallet-dip-provider/src/lib.rs | 2 +- pallets/pallet-dip-provider/src/traits.rs | 2 +- 11 files changed, 49 insertions(+), 66 deletions(-) delete mode 100644 crates/dip-support/src/v1.rs diff --git a/Cargo.lock b/Cargo.lock index 3640aabd5b..aa6e5b264a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4155,6 +4155,7 @@ version = "1.11.0-dev" dependencies = [ "did", "frame-support", + "frame-system", "pallet-dip-consumer", "parity-scale-codec", "scale-info", diff --git a/crates/dip-support/src/lib.rs b/crates/dip-support/src/lib.rs index fa388d032e..bde5dbfee3 100644 --- a/crates/dip-support/src/lib.rs +++ b/crates/dip-support/src/lib.rs @@ -20,8 +20,12 @@ #![cfg_attr(not(feature = "std"), no_std)] -// Export v1 behind a namespace and also as the latest -pub mod v1; -pub mod latest { - pub use crate::v1::*; +use frame_support::RuntimeDebug; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; + +#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen, RuntimeDebug)] +pub enum IdentityProofAction { + Updated(Identifier, Proof, Details), + Deleted(Identifier), } diff --git a/crates/dip-support/src/v1.rs b/crates/dip-support/src/v1.rs deleted file mode 100644 index 243e3c1561..0000000000 --- a/crates/dip-support/src/v1.rs +++ /dev/null @@ -1,27 +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; - -#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen, RuntimeDebug)] -pub enum IdentityProofAction { - Updated(Identifier, Proof, Details), - Deleted(Identifier), -} diff --git a/crates/kilt-dip-support/Cargo.toml b/crates/kilt-dip-support/Cargo.toml index 7f879b09f3..ebc86ee9b0 100644 --- a/crates/kilt-dip-support/Cargo.toml +++ b/crates/kilt-dip-support/Cargo.toml @@ -20,6 +20,7 @@ parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} # Substrate dependencies +frame-system.workspace = true frame-support.workspace = true sp-runtime.workspace = true sp-core.workspace = true @@ -33,6 +34,7 @@ std = [ "pallet-dip-consumer/std", "parity-scale-codec/std", "scale-info/std", + "frame-system/std", "frame-support/std", "sp-runtime/std", "sp-core/std", diff --git a/crates/kilt-dip-support/src/traits.rs b/crates/kilt-dip-support/src/traits.rs index be71e2b721..a5e0ad6941 100644 --- a/crates/kilt-dip-support/src/traits.rs +++ b/crates/kilt-dip-support/src/traits.rs @@ -16,7 +16,9 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +use sp_core::Get; use sp_runtime::traits::{CheckedAdd, One, Zero}; +use sp_std::marker::PhantomData; /// A trait for "bumpable" types, i.e., types that have some notion of order of /// its members. @@ -53,3 +55,26 @@ pub trait DidDipOriginFilter { fn check_call_origin_info(call: &Call, info: &Self::OriginInfo) -> Result; } + +pub struct GenesisProvider(PhantomData); + +impl Get for GenesisProvider +where + T: frame_system::Config, + T::BlockNumber: Zero, +{ + fn get() -> T::Hash { + frame_system::Pallet::::block_hash(T::BlockNumber::zero()) + } +} + +pub struct BlockNumberProvider(PhantomData); + +impl Get for BlockNumberProvider +where + T: frame_system::Config, +{ + fn get() -> T::BlockNumber { + frame_system::Pallet::::block_number() + } +} diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index 7ba705c0dd..b27875f3fe 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -17,35 +17,18 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use did::{did_details::DidVerificationKey, DidSignature, DidVerificationKeyRelationship, KeyIdOf}; -use frame_support::traits::{Contains, Get}; +use frame_support::traits::Contains; use kilt_dip_support::{ did::{DidSignatureAndCallVerifier, MerkleRevealedDidSignatureVerifier}, merkle::{DidMerkleProofVerifier, MerkleProof, ProofLeaf}, - traits::DidDipOriginFilter, + traits::{BlockNumberProvider, DidDipOriginFilter, GenesisProvider}, MerkleProofAndDidSignatureVerifier, }; use pallet_dip_consumer::traits::IdentityProofVerifier; -use sp_runtime::traits::Zero; use sp_std::vec::Vec; use crate::{AccountId, BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; -pub struct GenesisProvider; - -impl Get for GenesisProvider { - fn get() -> Hash { - frame_system::Pallet::::block_hash(BlockNumber::zero()) - } -} - -pub struct BlockNumberProvider; - -impl Get for BlockNumberProvider { - fn get() -> BlockNumber { - frame_system::Pallet::::block_number() - } -} - pub type MerkleProofVerifier = DidMerkleProofVerifier, BlockNumber, u128, 10>; pub type MerkleProofVerifierOutputOf = >::VerificationResult; @@ -54,10 +37,10 @@ pub type MerkleDidSignatureVerifierOf = MerkleRevealedDidSignatur Hash, u128, AccountId, - GenesisProvider, + GenesisProvider, Hash, MerkleProofVerifierOutputOf, - BlockNumberProvider, + BlockNumberProvider, // Signatures are valid for 50 blocks 50, >; diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index 82c151182c..e358eff8a5 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -17,7 +17,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use did::did_details::DidDetails; -use dip_support::latest::IdentityProofAction; +use dip_support::IdentityProofAction; use pallet_dip_provider::traits::{TxBuilder, XcmRouterDispatcher}; use parity_scale_codec::{Decode, Encode}; use runtime_common::dip::{did::DidIdentityProvider, merkle::DidMerkleRootGenerator}; diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index 625af89166..b30ed75c21 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -85,7 +85,7 @@ fn commit_identity() { // 3.2 Generate a DID signature let genesis_hash = ConsumerParachain::execute_with(|| frame_system::Pallet::::block_hash(BlockNumber::zero())); - let system_block = ConsumerParachain::execute_with(|| frame_system::Pallet::::block_number()); + let system_block = ConsumerParachain::execute_with(frame_system::Pallet::::block_number); let payload = ( call.clone(), 0u128, diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index 12ebe4931d..c071d5246c 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -21,9 +21,10 @@ #![cfg_attr(not(feature = "std"), no_std)] pub mod identity; -mod origin; pub mod traits; +mod origin; + pub use crate::{origin::*, pallet::*}; #[frame_support::pallet] @@ -36,7 +37,7 @@ pub mod pallet { use parity_scale_codec::MaxEncodedLen; use sp_std::boxed::Box; - use dip_support::latest::IdentityProofAction; + use dip_support::IdentityProofAction; use crate::{identity::IdentityDetails, traits::IdentityProofVerifier}; @@ -123,14 +124,8 @@ pub mod pallet { /// The origin this pallet creates after a user has provided a valid /// identity proof to dispatch other calls. #[pallet::origin] - pub type Origin = DipOrigin< - ::Identifier, - ::AccountId, - <::ProofVerifier as IdentityProofVerifier< - ::RuntimeCall, - ::Identifier, - >>::VerificationResult, - >; + pub type Origin = + DipOrigin<::Identifier, ::AccountId, VerificationResultOf>; // TODO: Benchmarking #[pallet::call] diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index acc9b93747..c826d191a7 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -33,7 +33,7 @@ pub mod pallet { use sp_std::{boxed::Box, fmt::Debug}; use xcm::{latest::prelude::*, VersionedMultiAsset, VersionedMultiLocation}; - use dip_support::latest::IdentityProofAction; + use dip_support::IdentityProofAction; use crate::traits::{IdentityProofDispatcher, IdentityProofGenerator, IdentityProvider, TxBuilder}; diff --git a/pallets/pallet-dip-provider/src/traits.rs b/pallets/pallet-dip-provider/src/traits.rs index 3b0e0c2342..fd9b90d046 100644 --- a/pallets/pallet-dip-provider/src/traits.rs +++ b/pallets/pallet-dip-provider/src/traits.rs @@ -16,7 +16,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use dip_support::latest::IdentityProofAction; +use dip_support::IdentityProofAction; use xcm::{latest::prelude::*, DoubleEncoded}; pub use identity_generation::*; From 0330e17f7aa77425820b7c9222b4df51fcbec21e Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 10 May 2023 15:47:44 +0200 Subject: [PATCH 19/26] Require genesis hash for DID signatures --- crates/kilt-dip-support/src/did.rs | 36 ++++++++++++------- dip-template/runtimes/dip-consumer/src/dip.rs | 4 +-- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index ac49baaf27..b0a4ddb9bf 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -36,18 +36,21 @@ use crate::{ /// previously-verified Merkle proof. It requires the `Details` type to /// implement the `Bump` trait to avoid replay attacks. The basic verification /// logic verifies that the signature has been generated over the encoded tuple -/// (call, identity details). Additional details can be added to the end of the -/// tuple by providing a `SignedExtraProvider`. +/// (call, identity details, submitter_address, submission_block_number, +/// genesis_hash). Additional details can be added to the end of the tuple by +/// providing a `SignedExtraProvider`. pub struct MerkleRevealedDidSignatureVerifier< BlockNumber, Digest, Details, AccountId, - SignedExtraProvider, - SignedExtra, MerkleProofEntries, BlockNumberProvider, const SIGNATURE_VALIDITY: u64, + GenesisHashProvider, + Hash, + SignedExtraProvider = (), + SignedExtra = (), >( #[allow(clippy::type_complexity)] PhantomData<( @@ -55,11 +58,13 @@ pub struct MerkleRevealedDidSignatureVerifier< Digest, Details, AccountId, - SignedExtraProvider, - SignedExtra, MerkleProofEntries, BlockNumberProvider, ConstU64, + GenesisHashProvider, + Hash, + SignedExtraProvider, + SignedExtra, )>, ); @@ -70,32 +75,38 @@ impl< Digest, Details, AccountId, - SignedExtraProvider, - SignedExtra, MerkleProofEntries, BlockNumberProvider, const SIGNATURE_VALIDITY: u64, + GenesisHashProvider, + Hash, + SignedExtraProvider, + SignedExtra, > IdentityProofVerifier for MerkleRevealedDidSignatureVerifier< BlockNumber, Digest, Details, AccountId, - SignedExtraProvider, - SignedExtra, MerkleProofEntries, BlockNumberProvider, SIGNATURE_VALIDITY, + GenesisHashProvider, + Hash, + SignedExtraProvider, + SignedExtra, > where AccountId: Encode, BlockNumber: Encode + CheckedSub + Into + PartialOrd + sp_std::fmt::Debug, Call: Encode, Digest: Encode, Details: Bump + Encode, - SignedExtraProvider: Get, - SignedExtra: Encode, MerkleProofEntries: AsRef<[ProofEntry]>, BlockNumberProvider: Get, + GenesisHashProvider: Get, + Hash: Encode, + SignedExtraProvider: Get, + SignedExtra: Encode, { // TODO: Error handling type Error = (); @@ -132,6 +143,7 @@ impl< proof_entry.details(), submitter, block_number, + GenesisHashProvider::get(), SignedExtraProvider::get(), ) .encode(); diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index b27875f3fe..dcc99ab48c 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -37,12 +37,12 @@ pub type MerkleDidSignatureVerifierOf = MerkleRevealedDidSignatur Hash, u128, AccountId, - GenesisProvider, - Hash, MerkleProofVerifierOutputOf, BlockNumberProvider, // Signatures are valid for 50 blocks 50, + GenesisProvider, + Hash, >; impl pallet_dip_consumer::Config for Runtime { From aa58be4444d4b9827fb6656d3a8b8c930f6eee16 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Fri, 12 May 2023 12:56:54 +0200 Subject: [PATCH 20/26] Fix block number selection in DID signature verification --- crates/kilt-dip-support/src/did.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index b0a4ddb9bf..225ef62181 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -142,7 +142,7 @@ impl< call, proof_entry.details(), submitter, - block_number, + &proof.1 .1, GenesisHashProvider::get(), SignedExtraProvider::get(), ) From e1f57f61faa7f4178e58bcc87d40eeeb9ed75769 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 17 May 2023 14:58:09 +0200 Subject: [PATCH 21/26] Add type for merkle leaves and DID signature --- crates/kilt-dip-support/src/did.rs | 40 ++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index 225ef62181..ce74f1f377 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -32,6 +32,16 @@ use crate::{ traits::{Bump, DidDipOriginFilter}, }; +pub struct TimeBoundDidSignature { + pub signature: DidSignature, + pub block_number: BlockNumber, +} + +pub struct MerkleLeavesAndDidSignature { + pub merkle_entries: MerkleEntries, + pub did_signature: TimeBoundDidSignature, +} + /// A type that verifies a DID signature over some DID keys revealed by a /// previously-verified Merkle proof. It requires the `Details` type to /// implement the `Bump` trait to avoid replay attacks. The basic verification @@ -112,7 +122,7 @@ impl< type Error = (); /// The proof must be a list of Merkle leaves that have been previously /// verified by the Merkle proof verifier, and the additional DID signature. - type Proof = (MerkleProofEntries, (DidSignature, BlockNumber)); + type Proof = MerkleLeavesAndDidSignature; /// The `Details` that are part of the identity details must implement the /// `Bump` trait. type IdentityDetails = IdentityDetails; @@ -130,25 +140,26 @@ impl< proof: &Self::Proof, ) -> Result { let block_number = BlockNumberProvider::get(); - let is_signature_fresh = if let Some(blocks_ago_from_now) = block_number.checked_sub(&proof.1 .1) { - // False if the signature is too old. - blocks_ago_from_now.into() <= SIGNATURE_VALIDITY - } else { - // Signature generated at a future time, not possible to verify. - false - }; + let is_signature_fresh = + if let Some(blocks_ago_from_now) = block_number.checked_sub(&proof.did_signature.block_number) { + // False if the signature is too old. + blocks_ago_from_now.into() <= SIGNATURE_VALIDITY + } else { + // Signature generated at a future time, not possible to verify. + false + }; ensure!(is_signature_fresh, ()); let encoded_payload = ( call, proof_entry.details(), submitter, - &proof.1 .1, + &proof.did_signature.block_number, GenesisHashProvider::get(), SignedExtraProvider::get(), ) .encode(); // Only consider verification keys from the set of revealed Merkle leaves. - let mut proof_verification_keys = proof.0.as_ref().iter().filter_map( + let mut proof_verification_keys = proof.merkle_entries.as_ref().iter().filter_map( |ProofEntry { key: DidPublicKeyDetails { key, .. }, relationship, @@ -156,15 +167,18 @@ impl< if let DidPublicKey::PublicVerificationKey(k) = key { Some(( k, - DidVerificationKeyRelationship::try_from(*relationship).expect("Should never fail."), + DidVerificationKeyRelationship::try_from(*relationship).expect("Should never fail to build a VerificationRelationship from the given DidKeyRelationship because we have already made sure the conditions hold."), )) } else { None } }, ); - let valid_signing_key = proof_verification_keys - .find(|(verification_key, _)| verification_key.verify_signature(&encoded_payload, &proof.1 .0).is_ok()); + let valid_signing_key = proof_verification_keys.find(|(verification_key, _)| { + verification_key + .verify_signature(&encoded_payload, &proof.did_signature.signature) + .is_ok() + }); if let Some((key, relationship)) = valid_signing_key { proof_entry.details.bump(); Ok((key.clone(), relationship)) From dee55515b8b211a959e960a36eb5597b85439849 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 17 May 2023 15:02:03 +0200 Subject: [PATCH 22/26] Fix typo --- crates/kilt-dip-support/src/did.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index ce74f1f377..8b0bcda8f9 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -192,7 +192,7 @@ impl< /// `MerkleRevealedDidSignatureVerifier`, and a call filtering logic based on /// the type of key used in the signature. /// Verification bails out early in case of invalid DID signatures. Otherwise, -/// the retrived key and its relationship is passed to the call verifier to do +/// the retrieved key and its relationship is passed to the call verifier to do /// some additional lookups on the call. /// The `CallVerifier` only performs internal checks, while all input and output /// types are taken from the provided `DidSignatureVerifier` type. From 6f8e7850d37e1fc7a6c270a4959449b49c858d09 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 17 May 2023 15:35:54 +0200 Subject: [PATCH 23/26] More elegant Bump implementation --- crates/kilt-dip-support/src/lib.rs | 2 +- crates/kilt-dip-support/src/traits.rs | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-support/src/lib.rs index 899fd48a05..814cb6257d 100644 --- a/crates/kilt-dip-support/src/lib.rs +++ b/crates/kilt-dip-support/src/lib.rs @@ -31,7 +31,7 @@ pub mod traits; /// A type that chains a Merkle proof verification with a DID signature /// verification. The required input of this type is a tuple (A, B) where A is /// the type of input required by the `MerkleProofVerifier` and B is a -/// `DidSignature. +/// `DidSignature`. /// The successful output of this type is the output type of the /// `MerkleProofVerifier`, meaning that DID signature verification happens /// internally and does not transform the result in any way. diff --git a/crates/kilt-dip-support/src/traits.rs b/crates/kilt-dip-support/src/traits.rs index a5e0ad6941..56d4dd09b1 100644 --- a/crates/kilt-dip-support/src/traits.rs +++ b/crates/kilt-dip-support/src/traits.rs @@ -20,6 +20,8 @@ use sp_core::Get; use sp_runtime::traits::{CheckedAdd, One, Zero}; use sp_std::marker::PhantomData; +// TODO: Switch to the `Incrementable` trait once it's added to the root of +// `frame_support`. /// A trait for "bumpable" types, i.e., types that have some notion of order of /// its members. pub trait Bump { @@ -34,11 +36,7 @@ where { // FIXME: Better implementation? fn bump(&mut self) { - if let Some(new) = self.checked_add(&Self::one()) { - *self = new; - } else { - *self = Self::zero(); - } + *self = self.checked_add(&Self::one()).unwrap_or_else(Self::zero); } } From e2eef36eeee5e5184644071a9c4f04727da07fac Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 17 May 2023 15:58:40 +0200 Subject: [PATCH 24/26] More changes --- crates/kilt-dip-support/src/did.rs | 22 +++++------ crates/kilt-dip-support/src/lib.rs | 39 ++++++++++--------- dip-template/runtimes/dip-consumer/src/dip.rs | 9 ++--- dip-template/runtimes/xcm-tests/src/tests.rs | 16 +++++--- pallets/pallet-dip-consumer/src/identity.rs | 9 ----- 5 files changed, 46 insertions(+), 49 deletions(-) diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index 8b0bcda8f9..d5602c8586 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -22,8 +22,9 @@ use did::{ }; use frame_support::ensure; use pallet_dip_consumer::{identity::IdentityDetails, traits::IdentityProofVerifier}; -use parity_scale_codec::Encode; -use sp_core::{ConstU64, Get}; +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_core::{ConstU64, Get, RuntimeDebug}; use sp_runtime::traits::CheckedSub; use sp_std::marker::PhantomData; @@ -32,11 +33,13 @@ use crate::{ traits::{Bump, DidDipOriginFilter}, }; +#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)] pub struct TimeBoundDidSignature { pub signature: DidSignature, pub block_number: BlockNumber, } +#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)] pub struct MerkleLeavesAndDidSignature { pub merkle_entries: MerkleEntries, pub did_signature: TimeBoundDidSignature, @@ -151,7 +154,7 @@ impl< ensure!(is_signature_fresh, ()); let encoded_payload = ( call, - proof_entry.details(), + &proof_entry.details, submitter, &proof.did_signature.block_number, GenesisHashProvider::get(), @@ -204,24 +207,21 @@ impl IdentityProofVerifier where DidSignatureVerifier: IdentityProofVerifier, - CallVerifier: DidDipOriginFilter< - Call, - OriginInfo = >::VerificationResult, - >, + CallVerifier: DidDipOriginFilter, { // FIXME: Better error handling type Error = (); /// The input proof is the same accepted by the `DidSignatureVerifier`. - type Proof = >::Proof; + type Proof = DidSignatureVerifier::Proof; /// The identity details are the same accepted by the /// `DidSignatureVerifier`. - type IdentityDetails = >::IdentityDetails; + type IdentityDetails = DidSignatureVerifier::IdentityDetails; /// The submitter address is the same accepted by the /// `DidSignatureVerifier`. - type Submitter = >::Submitter; + type Submitter = DidSignatureVerifier::Submitter; /// The verification result is the same accepted by the /// `DidSignatureVerifier`. - type VerificationResult = >::VerificationResult; + type VerificationResult = DidSignatureVerifier::VerificationResult; fn verify_proof_for_call_against_entry( call: &Call, diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-support/src/lib.rs index 814cb6257d..81e995c760 100644 --- a/crates/kilt-dip-support/src/lib.rs +++ b/crates/kilt-dip-support/src/lib.rs @@ -20,10 +20,11 @@ #![cfg_attr(not(feature = "std"), no_std)] -use ::did::DidSignature; use pallet_dip_consumer::traits::IdentityProofVerifier; use sp_std::marker::PhantomData; +use crate::did::MerkleLeavesAndDidSignature; + pub mod did; pub mod merkle; pub mod traits; @@ -49,24 +50,18 @@ where DidSignatureVerifier: IdentityProofVerifier< Call, Subject, - Proof = ( - >::VerificationResult, - (DidSignature, BlockNumber), - ), - IdentityDetails = >::IdentityDetails, - Submitter = >::Submitter, + Proof = MerkleLeavesAndDidSignature, + IdentityDetails = MerkleProofVerifier::IdentityDetails, + Submitter = MerkleProofVerifier::Submitter, >, { // FIXME: Better error handling type Error = (); // FIXME: Better type declaration - type Proof = ( - >::Proof, - (DidSignature, BlockNumber), - ); - type IdentityDetails = >::IdentityDetails; - type Submitter = >::Submitter; - type VerificationResult = >::VerificationResult; + type Proof = MerkleLeavesAndDidSignature; + type IdentityDetails = DidSignatureVerifier::IdentityDetails; + type Submitter = MerkleProofVerifier::Submitter; + type VerificationResult = MerkleProofVerifier::VerificationResult; fn verify_proof_for_call_against_entry( call: &Call, @@ -75,16 +70,24 @@ where proof_entry: &mut Self::IdentityDetails, proof: &Self::Proof, ) -> Result { - let merkle_proof_verification = - MerkleProofVerifier::verify_proof_for_call_against_entry(call, subject, submitter, proof_entry, &proof.0) - .map_err(|_| ())?; + let merkle_proof_verification = MerkleProofVerifier::verify_proof_for_call_against_entry( + call, + subject, + submitter, + proof_entry, + &proof.merkle_entries, + ) + .map_err(|_| ())?; DidSignatureVerifier::verify_proof_for_call_against_entry( call, subject, submitter, proof_entry, // FIXME: Remove `clone()` requirement - &(merkle_proof_verification.clone(), proof.1.clone()), + &MerkleLeavesAndDidSignature { + merkle_entries: merkle_proof_verification.clone(), + did_signature: proof.did_signature.clone(), + }, ) .map_err(|_| ())?; Ok(merkle_proof_verification) diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index dcc99ab48c..7c24493c47 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -16,10 +16,10 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::{did_details::DidVerificationKey, DidSignature, DidVerificationKeyRelationship, KeyIdOf}; +use did::{did_details::DidVerificationKey, DidVerificationKeyRelationship, KeyIdOf}; use frame_support::traits::Contains; use kilt_dip_support::{ - did::{DidSignatureAndCallVerifier, MerkleRevealedDidSignatureVerifier}, + did::{DidSignatureAndCallVerifier, MerkleLeavesAndDidSignature, MerkleRevealedDidSignatureVerifier}, merkle::{DidMerkleProofVerifier, MerkleProof, ProofLeaf}, traits::{BlockNumberProvider, DidDipOriginFilter, GenesisProvider}, MerkleProofAndDidSignatureVerifier, @@ -49,10 +49,7 @@ impl pallet_dip_consumer::Config for Runtime { type DipCallOriginFilter = PreliminaryDipOriginFilter; type Identifier = DidIdentifier; type IdentityDetails = u128; - type Proof = ( - MerkleProof>, ProofLeaf>, - (DidSignature, BlockNumber), - ); + type Proof = MerkleLeavesAndDidSignature>, ProofLeaf>, BlockNumber>; type ProofDigest = Hash; type ProofVerifier = MerkleProofAndDidSignatureVerifier< BlockNumber, diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index b30ed75c21..907b2bcd5b 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -21,7 +21,10 @@ use super::*; use did::{Did, DidSignature}; use frame_support::{assert_ok, weights::Weight}; use frame_system::RawOrigin; -use kilt_dip_support::merkle::MerkleProof; +use kilt_dip_support::{ + did::{MerkleLeavesAndDidSignature, TimeBoundDidSignature}, + merkle::MerkleProof, +}; use pallet_did_lookup::linkable_account::LinkableAccountId; use parity_scale_codec::Encode; use runtime_common::dip::merkle::{CompleteMerkleProof, DidMerkleRootGenerator}; @@ -100,13 +103,16 @@ fn commit_identity() { assert_ok!(DipConsumer::dispatch_as( RawOrigin::Signed(para::consumer::DISPATCHER_ACCOUNT).into(), did.clone(), - ( - MerkleProof { + MerkleLeavesAndDidSignature { + merkle_entries: MerkleProof { blinded: proof.blinded, revealed: proof.revealed, }, - (signature, system_block) - ), + did_signature: TimeBoundDidSignature { + signature, + block_number: system_block + } + }, Box::new(call), )); // Verify the account -> DID link exists and contains the right information diff --git a/pallets/pallet-dip-consumer/src/identity.rs b/pallets/pallet-dip-consumer/src/identity.rs index e4b8350b31..3e244abf6b 100644 --- a/pallets/pallet-dip-consumer/src/identity.rs +++ b/pallets/pallet-dip-consumer/src/identity.rs @@ -30,15 +30,6 @@ pub struct IdentityDetails { pub details: Details, } -impl IdentityDetails { - pub fn digest(&self) -> &Digest { - &self.digest - } - pub fn details(&self) -> &Details { - &self.details - } -} - impl IdentityDetails where Details: Default, From b0aa9918456e6c554933d5812b781ab56733d68b Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 17 May 2023 16:08:54 +0200 Subject: [PATCH 25/26] Refactoring types --- crates/kilt-dip-support/src/did.rs | 4 ++-- crates/kilt-dip-support/src/lib.rs | 8 ++++---- dip-template/runtimes/dip-consumer/src/dip.rs | 4 ++-- dip-template/runtimes/xcm-tests/src/tests.rs | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-support/src/did.rs index d5602c8586..07bd50d2e6 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-support/src/did.rs @@ -40,7 +40,7 @@ pub struct TimeBoundDidSignature { } #[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)] -pub struct MerkleLeavesAndDidSignature { +pub struct MerkleEntriesAndDidSignature { pub merkle_entries: MerkleEntries, pub did_signature: TimeBoundDidSignature, } @@ -125,7 +125,7 @@ impl< type Error = (); /// The proof must be a list of Merkle leaves that have been previously /// verified by the Merkle proof verifier, and the additional DID signature. - type Proof = MerkleLeavesAndDidSignature; + type Proof = MerkleEntriesAndDidSignature; /// The `Details` that are part of the identity details must implement the /// `Bump` trait. type IdentityDetails = IdentityDetails; diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-support/src/lib.rs index 81e995c760..c2f18ba0f8 100644 --- a/crates/kilt-dip-support/src/lib.rs +++ b/crates/kilt-dip-support/src/lib.rs @@ -23,7 +23,7 @@ use pallet_dip_consumer::traits::IdentityProofVerifier; use sp_std::marker::PhantomData; -use crate::did::MerkleLeavesAndDidSignature; +use crate::did::MerkleEntriesAndDidSignature; pub mod did; pub mod merkle; @@ -50,7 +50,7 @@ where DidSignatureVerifier: IdentityProofVerifier< Call, Subject, - Proof = MerkleLeavesAndDidSignature, + Proof = MerkleEntriesAndDidSignature, IdentityDetails = MerkleProofVerifier::IdentityDetails, Submitter = MerkleProofVerifier::Submitter, >, @@ -58,7 +58,7 @@ where // FIXME: Better error handling type Error = (); // FIXME: Better type declaration - type Proof = MerkleLeavesAndDidSignature; + type Proof = MerkleEntriesAndDidSignature; type IdentityDetails = DidSignatureVerifier::IdentityDetails; type Submitter = MerkleProofVerifier::Submitter; type VerificationResult = MerkleProofVerifier::VerificationResult; @@ -84,7 +84,7 @@ where submitter, proof_entry, // FIXME: Remove `clone()` requirement - &MerkleLeavesAndDidSignature { + &MerkleEntriesAndDidSignature { merkle_entries: merkle_proof_verification.clone(), did_signature: proof.did_signature.clone(), }, diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index 7c24493c47..639ff16e6b 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -19,7 +19,7 @@ use did::{did_details::DidVerificationKey, DidVerificationKeyRelationship, KeyIdOf}; use frame_support::traits::Contains; use kilt_dip_support::{ - did::{DidSignatureAndCallVerifier, MerkleLeavesAndDidSignature, MerkleRevealedDidSignatureVerifier}, + did::{DidSignatureAndCallVerifier, MerkleEntriesAndDidSignature, MerkleRevealedDidSignatureVerifier}, merkle::{DidMerkleProofVerifier, MerkleProof, ProofLeaf}, traits::{BlockNumberProvider, DidDipOriginFilter, GenesisProvider}, MerkleProofAndDidSignatureVerifier, @@ -49,7 +49,7 @@ impl pallet_dip_consumer::Config for Runtime { type DipCallOriginFilter = PreliminaryDipOriginFilter; type Identifier = DidIdentifier; type IdentityDetails = u128; - type Proof = MerkleLeavesAndDidSignature>, ProofLeaf>, BlockNumber>; + type Proof = MerkleEntriesAndDidSignature>, ProofLeaf>, BlockNumber>; type ProofDigest = Hash; type ProofVerifier = MerkleProofAndDidSignatureVerifier< BlockNumber, diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index 907b2bcd5b..a868a64ea1 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -22,7 +22,7 @@ use did::{Did, DidSignature}; use frame_support::{assert_ok, weights::Weight}; use frame_system::RawOrigin; use kilt_dip_support::{ - did::{MerkleLeavesAndDidSignature, TimeBoundDidSignature}, + did::{MerkleEntriesAndDidSignature, TimeBoundDidSignature}, merkle::MerkleProof, }; use pallet_did_lookup::linkable_account::LinkableAccountId; @@ -103,7 +103,7 @@ fn commit_identity() { assert_ok!(DipConsumer::dispatch_as( RawOrigin::Signed(para::consumer::DISPATCHER_ACCOUNT).into(), did.clone(), - MerkleLeavesAndDidSignature { + MerkleEntriesAndDidSignature { merkle_entries: MerkleProof { blinded: proof.blinded, revealed: proof.revealed, From 97ac3f9542cd7202383c0ba83a5309ccd935823e Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Mon, 22 May 2023 16:04:26 +0200 Subject: [PATCH 26/26] Replace from_digest with From trait impl --- pallets/pallet-dip-consumer/src/identity.rs | 6 +++--- pallets/pallet-dip-consumer/src/lib.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pallets/pallet-dip-consumer/src/identity.rs b/pallets/pallet-dip-consumer/src/identity.rs index 3e244abf6b..eed39f68d4 100644 --- a/pallets/pallet-dip-consumer/src/identity.rs +++ b/pallets/pallet-dip-consumer/src/identity.rs @@ -30,13 +30,13 @@ pub struct IdentityDetails { pub details: Details, } -impl IdentityDetails +impl From for IdentityDetails where Details: Default, { - pub fn from_digest(digest: Digest) -> Self { + fn from(value: Digest) -> Self { Self { - digest, + digest: value, details: Details::default(), } } diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index c071d5246c..f9712bf5a0 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -144,7 +144,7 @@ pub mod pallet { &identifier, |entry: &mut Option< IdentityDetails<::ProofDigest, ::IdentityDetails>, - >| { *entry = Some(IdentityDetails::from_digest(proof.clone())) }, + >| { *entry = Some(proof.clone().into()) }, ); Ok::<_, Error>(Event::::IdentityInfoUpdated(identifier, proof)) }