From 756ec48d12ff9d73e0e7aa8eb0b175ca42ae10b7 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Mon, 20 Mar 2023 13:36:51 +0100 Subject: [PATCH 01/23] wip --- Cargo.lock | 4 + Cargo.toml | 1 + crates/dip/src/lib.rs | 26 +++- crates/dip/src/v1.rs | 5 +- pallets/pallet-dip-receiver/src/lib.rs | 6 +- pallets/pallet-dip-receiver/src/traits.rs | 20 +-- runtimes/common/Cargo.toml | 13 +- runtimes/common/src/dip.rs | 165 ++++++++++++++++++++++ runtimes/common/src/lib.rs | 1 + 9 files changed, 224 insertions(+), 17 deletions(-) create mode 100644 runtimes/common/src/dip.rs diff --git a/Cargo.lock b/Cargo.lock index 6ded9576f0..c982e35ae5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9268,6 +9268,7 @@ dependencies = [ "cumulus-primitives-core", "delegation", "did", + "dip-support", "frame-support", "frame-system", "kilt-asset-dids", @@ -9276,6 +9277,8 @@ dependencies = [ "pallet-authorship", "pallet-balances", "pallet-did-lookup", + "pallet-dip-receiver", + "pallet-dip-sender", "pallet-inflation", "pallet-membership", "pallet-transaction-payment", @@ -9291,6 +9294,7 @@ dependencies = [ "sp-io", "sp-runtime", "sp-std", + "sp-trie", "xcm", "xcm-builder", "xcm-executor", diff --git a/Cargo.toml b/Cargo.toml index ffd1fdfd70..3cb83ae5fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -136,6 +136,7 @@ sp-session = {git = "https://github.com/paritytech/substrate", default-features sp-staking = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.38"} sp-std = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.38"} sp-transaction-pool = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.38"} +sp-trie = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.38"} sp-version = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.38"} sp-weights = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.38"} try-runtime-cli = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.38"} diff --git a/crates/dip/src/lib.rs b/crates/dip/src/lib.rs index e71b1697d5..db9c321c7e 100644 --- a/crates/dip/src/lib.rs +++ b/crates/dip/src/lib.rs @@ -45,13 +45,31 @@ impl From { +pub enum VersionedIdentityProof { #[codec(index = 1)] - V1(v1::Proof), + V1(v1::Proof), } -impl From> for VersionedIdentityProof { - fn from(value: v1::Proof) -> Self { +impl From> + for VersionedIdentityProof +{ + fn from(value: v1::Proof) -> Self { Self::V1(value) } } + +impl TryFrom> + for v1::Proof +{ + // Proper error handling + type Error = (); + + fn try_from(value: VersionedIdentityProof) -> Result { + #[allow(irrefutable_let_patterns)] + if let VersionedIdentityProof::V1(v1::Proof { blinded, revealed }) = value { + Ok(Self { blinded, revealed }) + } else { + Err(()) + } + } +} diff --git a/crates/dip/src/v1.rs b/crates/dip/src/v1.rs index c57ebec201..0f5e0fc148 100644 --- a/crates/dip/src/v1.rs +++ b/crates/dip/src/v1.rs @@ -28,4 +28,7 @@ pub enum IdentityProofAction { } #[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo, Default)] -pub struct Proof(Vec<(LeafKey, LeafValue)>); +pub struct Proof { + pub blinded: BlindedValue, + pub revealed: Vec<(LeafKey, LeafValue)>, +} diff --git a/pallets/pallet-dip-receiver/src/lib.rs b/pallets/pallet-dip-receiver/src/lib.rs index 551b6bc80a..bb537c3cf3 100644 --- a/pallets/pallet-dip-receiver/src/lib.rs +++ b/pallets/pallet-dip-receiver/src/lib.rs @@ -39,7 +39,7 @@ pub mod pallet { use crate::traits::IdentityProofVerifier; pub type VersionedIdentityProofOf = - VersionedIdentityProof<::ProofLeafKey, ::ProofLeafValue>; + VersionedIdentityProof<::BlindedValue, ::ProofLeafKey, ::ProofLeafValue>; const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); @@ -52,13 +52,15 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { type Identifier: Parameter + MaxEncodedLen; + type BlindedValue: Parameter; type ProofLeafKey: Parameter; type ProofLeafValue: Parameter; type ProofDigest: Parameter + MaxEncodedLen; type ProofVerifier: IdentityProofVerifier< - ProofDigest = Self::ProofDigest, + BlindedValue = Self::BlindedValue, LeafKey = Self::ProofLeafKey, LeafValue = Self::ProofLeafValue, + ProofDigest = Self::ProofDigest, >; type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin>; type RuntimeEvent: From> + IsType<::RuntimeEvent>; diff --git a/pallets/pallet-dip-receiver/src/traits.rs b/pallets/pallet-dip-receiver/src/traits.rs index 460dc48b0b..6a514aef76 100644 --- a/pallets/pallet-dip-receiver/src/traits.rs +++ b/pallets/pallet-dip-receiver/src/traits.rs @@ -20,31 +20,35 @@ use dip_support::VersionedIdentityProof; use sp_std::marker::PhantomData; pub trait IdentityProofVerifier { - type ProofDigest; + type BlindedValue; + type Error; type LeafKey; type LeafValue; + type ProofDigest; type VerificationResult; - type Error; fn verify_proof_against_digest( - proof: VersionedIdentityProof, + proof: VersionedIdentityProof, digest: Self::ProofDigest, ) -> Result; } // Always returns success. -pub struct SuccessfulProofVerifier(PhantomData<(ProofDigest, LeafKey, LeafValue)>); -impl IdentityProofVerifier - for SuccessfulProofVerifier +pub struct SuccessfulProofVerifier( + PhantomData<(ProofDigest, LeafKey, LeafValue, BlindedValue)>, +); +impl IdentityProofVerifier + for SuccessfulProofVerifier { - type ProofDigest = ProofDigest; + type BlindedValue = BlindedValue; type Error = (); type LeafKey = LeafKey; type LeafValue = LeafValue; + type ProofDigest = ProofDigest; type VerificationResult = (); fn verify_proof_against_digest( - _proof: VersionedIdentityProof, + _proof: VersionedIdentityProof, _digest: Self::ProofDigest, ) -> Result { Ok(()) diff --git a/runtimes/common/Cargo.toml b/runtimes/common/Cargo.toml index 97b5fc6f33..8399e42faf 100644 --- a/runtimes/common/Cargo.toml +++ b/runtimes/common/Cargo.toml @@ -25,8 +25,11 @@ smallvec.workspace = true attestation.workspace = true ctype.workspace = true delegation = {workspace = true, optional = true} -did = {workspace = true, optional = true} +did.workspace = true +dip-support.workspace = true pallet-did-lookup = {workspace = true, optional = true} +pallet-dip-receiver.workspace = true +pallet-dip-sender.workspace = true pallet-inflation = {workspace = true, optional = true} kilt-support.workspace = true parachain-staking.workspace = true @@ -45,6 +48,7 @@ sp-core.workspace = true sp-io.workspace = true sp-runtime.workspace = true sp-std.workspace = true +sp-trie.workspace = true # Cumulus dependencies cumulus-primitives-core.workspace = true @@ -78,6 +82,8 @@ std = [ "codec/std", "ctype/std", "cumulus-primitives-core/std", + "did/std", + "dip-support/std", "frame-support/std", "frame-system/std", "kilt-asset-dids/std", @@ -85,6 +91,8 @@ std = [ "log/std", "pallet-authorship/std", "pallet-balances/std", + "pallet-dip-receiver/std", + "pallet-dip-sender/std", "pallet-membership/std", "pallet-transaction-payment/std", "parachain-staking/std", @@ -96,6 +104,7 @@ std = [ "sp-io/std", "sp-runtime/std", "sp-std/std", + "sp-trie/std", "xcm-builder/std", "xcm-executor/std", "xcm/std", @@ -103,7 +112,7 @@ std = [ try-runtime = [ "attestation/try-runtime", "delegation", - "did", + "did/try-runtime", "frame-support/try-runtime", "frame-system/try-runtime", "kilt-support/try-runtime", diff --git a/runtimes/common/src/dip.rs b/runtimes/common/src/dip.rs new file mode 100644 index 0000000000..488885ef7f --- /dev/null +++ b/runtimes/common/src/dip.rs @@ -0,0 +1,165 @@ +// 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_std::marker::PhantomData; + +pub mod sender { + use super::*; + + use codec::Encode; + use did::did_details::{DidDetails, DidPublicKeyDetails}; + use pallet_dip_sender::traits::{IdentityProofGenerator, IdentityProvider}; + + pub(crate) const AUTH_KEY_LEAF: &[u8] = b"auth"; + pub(crate) const ATT_KEY_LEAF: &[u8] = b"att"; + pub(crate) const DEL_KEY_LEAF: &[u8] = b"del"; + pub(crate) const ENC_KEY_PREFIX: &[u8] = b"enc-"; + pub(crate) const PUB_KEY_PREFIX: &[u8] = b"pub-"; + + pub struct DidMerkleRootGenerator(PhantomData); + + impl IdentityProofGenerator, T::Hash> for DidMerkleRootGenerator + where + T: did::Config, + { + // TODO: Proper error handling + type Error = (); + + fn generate_proof(_identifier: &T::DidIdentifier, identity: &DidDetails) -> Result { + use sp_trie::{LayoutV1, MemoryDB, TrieDBMutBuilder, TrieHash, TrieMut}; + + let mut db = MemoryDB::default(); + let mut trie = TrieHash::>::default(); + let mut trie_builder = TrieDBMutBuilder::>::new(&mut db, &mut trie).build(); + + // Authentication key + trie_builder + .insert(AUTH_KEY_LEAF, identity.authentication_key.encode().as_slice()) + .map_err(|_| ())?; + // Attestation key + if let Some(ref att_key) = identity.attestation_key { + trie_builder + .insert(ATT_KEY_LEAF, att_key.encode().as_slice()) + .map_err(|_| ())?; + }; + // Delegation key + if let Some(ref del_key) = identity.delegation_key { + trie_builder + .insert(DEL_KEY_LEAF, del_key.encode().as_slice()) + .map_err(|_| ())?; + }; + // Key agreement keys + identity + .key_agreement_keys + .iter() + .enumerate() + .try_for_each(|(i, id)| -> Result<(), ()> { + // Key leaf = "enc-" + let key_leaf = [ENC_KEY_PREFIX, i.to_be_bytes().as_slice()].concat(); + trie_builder.insert(&key_leaf, id.encode().as_slice()).map_err(|_| ())?; + Ok(()) + })?; + // Public keys + identity.public_keys.iter().enumerate().try_for_each( + |(i, (id, DidPublicKeyDetails { key: public_key, .. }))| -> Result<(), ()> { + // Key leaf = "pub-" + let key_leaf = [PUB_KEY_PREFIX, i.to_be_bytes().as_slice()].concat(); + trie_builder + .insert(&key_leaf, (id, public_key).encode().as_slice()) + .map_err(|_| ())?; + Ok(()) + }, + )?; + trie_builder.commit(); + Ok(trie_builder.root().to_owned()) + } + } + + 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, ()))), + (None, Some(_)) => Ok(None), + _ => Err(()), + } + } + } +} + +pub mod receiver { + use super::*; + + use did::{did_details::DidPublicKey, DidVerificationKeyRelationship}; + use dip_support::VersionedIdentityProof; + use pallet_dip_receiver::traits::IdentityProofVerifier; + use sp_trie::LayoutV1; + + pub struct ProofEntry { + key: DidPublicKey, + verification_relationship: DidVerificationKeyRelationship, + } + + pub struct VerificationResult(Vec); + + pub struct DidMerkleProofVerifier(PhantomData); + + impl IdentityProofVerifier for DidMerkleProofVerifier + where + T: did::Config, + { + // TODO: Proper error handling + type BlindedValue = Vec>; + type Error = (); + type LeafKey = Vec; + type LeafValue = Option>; + type ProofDigest = T::Hash; + type VerificationResult = VerificationResult; + + fn verify_proof_against_digest( + proof: VersionedIdentityProof, + digest: Self::ProofDigest, + ) -> Result { + use crate::dip::sender::{ATT_KEY_LEAF, AUTH_KEY_LEAF, DEL_KEY_LEAF, ENC_KEY_PREFIX, PUB_KEY_PREFIX}; + use dip_support::v1; + use sp_trie::verify_trie_proof; + + let proof: v1::Proof<_, _, _> = proof.try_into()?; + verify_trie_proof::, _, _, _>(&digest, &proof.blinded, &proof.revealed) + .map_err(|_| ())?; + let mut revealed_leafs: Vec = vec![]; + + let revealed_leafs: Vec = proof.revealed.iter().map(|(key, value)| match (key) { + _ => ProofEntry { + key: value.try_into(), + verification_relationship: DidVerificationKeyRelationship::Authentication, + }, + }); + } + } +} diff --git a/runtimes/common/src/lib.rs b/runtimes/common/src/lib.rs index d627b067a9..e94d361b43 100644 --- a/runtimes/common/src/lib.rs +++ b/runtimes/common/src/lib.rs @@ -43,6 +43,7 @@ use sp_std::marker::PhantomData; pub mod assets; pub mod authorization; pub mod constants; +pub mod dip; pub mod errors; pub mod fees; pub mod migrations; From 40a0c28ef0dae26d705f52fe9658f84a4ac2fafd Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Tue, 21 Mar 2023 11:30:46 +0100 Subject: [PATCH 02/23] wip pt. 2 --- runtimes/common/src/dip.rs | 166 ++++++++++++++++++++++++++++--------- 1 file changed, 125 insertions(+), 41 deletions(-) diff --git a/runtimes/common/src/dip.rs b/runtimes/common/src/dip.rs index 488885ef7f..01765be469 100644 --- a/runtimes/common/src/dip.rs +++ b/runtimes/common/src/dip.rs @@ -21,15 +21,63 @@ use sp_std::marker::PhantomData; pub mod sender { use super::*; - use codec::Encode; - use did::did_details::{DidDetails, DidPublicKeyDetails}; + use codec::{Decode, Encode}; + use did::{ + did_details::{DidDetails, DidPublicKeyDetails}, + DidVerificationKeyRelationship, KeyIdOf, + }; use pallet_dip_sender::traits::{IdentityProofGenerator, IdentityProvider}; - pub(crate) const AUTH_KEY_LEAF: &[u8] = b"auth"; - pub(crate) const ATT_KEY_LEAF: &[u8] = b"att"; - pub(crate) const DEL_KEY_LEAF: &[u8] = b"del"; - pub(crate) const ENC_KEY_PREFIX: &[u8] = b"enc-"; - pub(crate) const PUB_KEY_PREFIX: &[u8] = b"pub-"; + pub enum MerkleLeaf { + VerificationKey(KeyIdOf, DidVerificationKeyRelationship), + EncryptionKey(KeyIdOf), + PublicKey(KeyIdOf, DidPublicKeyDetails), + } + + impl MerkleLeaf + where + T: did::Config, + { + pub fn key(&self) -> Vec { + match self { + MerkleLeaf::VerificationKey(_, relationship) => relationship.encode(), + MerkleLeaf::EncryptionKey(key_id) => key_id.encode(), + MerkleLeaf::PublicKey(key_id, _) => key_id.encode(), + } + } + + pub fn value(&self) -> Vec { + match self { + MerkleLeaf::VerificationKey(key_id, relationship) => key_id.encode(), + MerkleLeaf::EncryptionKey(key_id) => key_id.encode(), + MerkleLeaf::PublicKey(_, public_key) => public_key.encode(), + } + } + } + + impl TryFrom<(Vec, Vec)> for MerkleLeaf + where + T: did::Config, + { + // TODO: Proper error handling + type Error = (); + + fn try_from((key, value): (Vec, Vec)) -> Result { + let (key, value) = (&mut key.as_slice(), &mut value.as_slice()); + if let (Ok(relationship), Ok(key_id)) = + (DidVerificationKeyRelationship::decode(key), KeyIdOf::::decode(value)) + { + Ok(Self::VerificationKey(key_id, relationship)) + } else if let (Ok(key_id), Ok(_)) = (KeyIdOf::::decode(key), KeyIdOf::::decode(value)) { + Ok(Self::EncryptionKey(key_id)) + } else if let (Ok(key_id), Ok(public_key)) = (KeyIdOf::::decode(key), DidPublicKeyDetails::decode(value)) + { + Ok(Self::PublicKey(key_id, public_key)) + } else { + Err(()) + } + } + } pub struct DidMerkleRootGenerator(PhantomData); @@ -48,43 +96,66 @@ pub mod sender { let mut trie_builder = TrieDBMutBuilder::>::new(&mut db, &mut trie).build(); // Authentication key + let auth_key_leaf = MerkleLeaf::::VerificationKey( + identity.authentication_key, + DidVerificationKeyRelationship::Authentication, + ); trie_builder - .insert(AUTH_KEY_LEAF, identity.authentication_key.encode().as_slice()) + .insert( + auth_key_leaf.key().encode().as_slice(), + auth_key_leaf.value().encode().as_slice(), + ) .map_err(|_| ())?; - // Attestation key - if let Some(ref att_key) = identity.attestation_key { + // Attestation key: (key relationship, key id) + if let Some(att_key_id) = identity.attestation_key { + let att_key_leaf = + MerkleLeaf::::VerificationKey(att_key_id, DidVerificationKeyRelationship::AssertionMethod); trie_builder - .insert(ATT_KEY_LEAF, att_key.encode().as_slice()) + .insert( + att_key_leaf.key().encode().as_slice(), + att_key_leaf.value().encode().as_slice(), + ) .map_err(|_| ())?; }; - // Delegation key - if let Some(ref del_key) = identity.delegation_key { + // Delegation key: (key relationship, key id) + if let Some(del_key_id) = identity.delegation_key { + let del_key_leaf = + MerkleLeaf::::VerificationKey(del_key_id, DidVerificationKeyRelationship::CapabilityDelegation); trie_builder - .insert(DEL_KEY_LEAF, del_key.encode().as_slice()) + .insert( + del_key_leaf.key().encode().as_slice(), + del_key_leaf.value().encode().as_slice(), + ) .map_err(|_| ())?; }; - // Key agreement keys + // Key agreement keys [(enc-, key id)] identity .key_agreement_keys - .iter() - .enumerate() - .try_for_each(|(i, id)| -> Result<(), ()> { - // Key leaf = "enc-" - let key_leaf = [ENC_KEY_PREFIX, i.to_be_bytes().as_slice()].concat(); - trie_builder.insert(&key_leaf, id.encode().as_slice()).map_err(|_| ())?; + .into_iter() + .try_for_each(|id| -> Result<(), ()> { + let enc_key_leaf = MerkleLeaf::::EncryptionKey(id); + trie_builder + .insert( + enc_key_leaf.key().encode().as_slice(), + enc_key_leaf.value().encode().as_slice(), + ) + .map_err(|_| ())?; Ok(()) })?; - // Public keys - identity.public_keys.iter().enumerate().try_for_each( - |(i, (id, DidPublicKeyDetails { key: public_key, .. }))| -> Result<(), ()> { - // Key leaf = "pub-" - let key_leaf = [PUB_KEY_PREFIX, i.to_be_bytes().as_slice()].concat(); + // Public keys: [(key id, public key)] + identity + .public_keys + .into_iter() + .try_for_each(|(id, key_details)| -> Result<(), ()> { + let pub_key_leaf = MerkleLeaf::::PublicKey(id, key_details); trie_builder - .insert(&key_leaf, (id, public_key).encode().as_slice()) + .insert( + pub_key_leaf.key().encode().as_slice(), + pub_key_leaf.value().encode().as_slice(), + ) .map_err(|_| ())?; Ok(()) - }, - )?; + })?; trie_builder.commit(); Ok(trie_builder.root().to_owned()) } @@ -113,11 +184,14 @@ pub mod sender { } pub mod receiver { + use crate::dip::sender::MerkleLeaf; + use super::*; use did::{did_details::DidPublicKey, DidVerificationKeyRelationship}; use dip_support::VersionedIdentityProof; use pallet_dip_receiver::traits::IdentityProofVerifier; + use sp_std::vec::Vec; use sp_trie::LayoutV1; pub struct ProofEntry { @@ -137,7 +211,7 @@ pub mod receiver { type BlindedValue = Vec>; type Error = (); type LeafKey = Vec; - type LeafValue = Option>; + type LeafValue = Vec; type ProofDigest = T::Hash; type VerificationResult = VerificationResult; @@ -145,21 +219,31 @@ pub mod receiver { proof: VersionedIdentityProof, digest: Self::ProofDigest, ) -> Result { - use crate::dip::sender::{ATT_KEY_LEAF, AUTH_KEY_LEAF, DEL_KEY_LEAF, ENC_KEY_PREFIX, PUB_KEY_PREFIX}; use dip_support::v1; use sp_trie::verify_trie_proof; let proof: v1::Proof<_, _, _> = proof.try_into()?; - verify_trie_proof::, _, _, _>(&digest, &proof.blinded, &proof.revealed) - .map_err(|_| ())?; - let mut revealed_leafs: Vec = vec![]; - - let revealed_leafs: Vec = proof.revealed.iter().map(|(key, value)| match (key) { - _ => ProofEntry { - key: value.try_into(), - verification_relationship: DidVerificationKeyRelationship::Authentication, - }, - }); + verify_trie_proof::, _, _, _>( + &digest, + &proof.blinded, + proof.revealed.iter().map(|(key, value)| (key, Some(value))).collect(), + ) + .map_err(|_| ())?; + + let revealed_leaves: Vec> = proof + .revealed + .iter() + .map(|(key, value)| MerkleLeaf::try_from((*key, *value)).expect("Error.")) + .collect(); + // .map(|(key, value)| match key.as_ref() { + // AUTH_KEY_LEAF => ( + // KeyIdOf::::from(key.as_slice()), + // DidVerificationKeyRelationship::Authentication, + // ), + // ATT_KEY_LEAF => (*key, + // DidVerificationKeyRelationship::AssertionMethod), DEL_KEY_LEAF => + // (*key, DidVerificationKeyRelationship::CapabilityDelegation), }) + // .collect(); } } } From 4d5212da3ab71d3c59406856b5a95a515e8285a7 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 22 Mar 2023 15:40:54 +0100 Subject: [PATCH 03/23] Code compiling --- Cargo.lock | 2 + dip-template/runtimes/dip-receiver/Cargo.toml | 2 + dip-template/runtimes/dip-receiver/src/dip.rs | 18 +- dip-template/runtimes/dip-receiver/src/lib.rs | 7 +- dip-template/runtimes/dip-sender/Cargo.toml | 2 + dip-template/runtimes/dip-sender/src/dip.rs | 29 +- pallets/pallet-dip-sender/src/lib.rs | 8 +- pallets/pallet-dip-sender/src/traits.rs | 15 +- runtimes/common/src/dip.rs | 276 +++++++++++------- 9 files changed, 210 insertions(+), 149 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c982e35ae5..4f9e4a5963 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2268,6 +2268,7 @@ dependencies = [ "parachain-info", "parity-scale-codec", "polkadot-parachain", + "runtime-common", "scale-info", "sp-api", "sp-block-builder", @@ -2367,6 +2368,7 @@ dependencies = [ "pallet-xcm", "parachain-info", "parity-scale-codec", + "runtime-common", "scale-info", "sp-api", "sp-block-builder", diff --git a/dip-template/runtimes/dip-receiver/Cargo.toml b/dip-template/runtimes/dip-receiver/Cargo.toml index 3b559bb0ca..b40065c863 100644 --- a/dip-template/runtimes/dip-receiver/Cargo.toml +++ b/dip-template/runtimes/dip-receiver/Cargo.toml @@ -20,6 +20,7 @@ scale-info = {workspace = true, features = ["derive"]} # DIP pallet-did-lookup.workspace = true pallet-dip-receiver.workspace = true +runtime-common.workspace = true # Substrate frame-executive.workspace = true @@ -74,6 +75,7 @@ std = [ "scale-info/std", "pallet-did-lookup/std", "pallet-dip-receiver/std", + "runtime-common/std", "frame-executive/std", "frame-support/std", "frame-system/std", diff --git a/dip-template/runtimes/dip-receiver/src/dip.rs b/dip-template/runtimes/dip-receiver/src/dip.rs index b36d425abd..60c75d1d2e 100644 --- a/dip-template/runtimes/dip-receiver/src/dip.rs +++ b/dip-template/runtimes/dip-receiver/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 pallet_dip_receiver::traits::SuccessfulProofVerifier; +use runtime_common::dip::{receiver::DidMerkleProofVerifier, LeafKey, LeafValue}; +use sp_std::vec::Vec; -use crate::{DidIdentifier, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; +use crate::{BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; impl pallet_dip_receiver::Config for Runtime { + type BlindedValue = Vec>; type Identifier = DidIdentifier; - // TODO: Change with right one - type ProofDigest = [u8; 32]; - // TODO: Change with right one - type ProofLeafKey = [u8; 4]; - // TODO: Change with right one - type ProofLeafValue = [u8; 4]; - // TODO: Change with right one - type ProofVerifier = SuccessfulProofVerifier; + type ProofDigest = Hash; + type ProofLeafKey = LeafKey; + type ProofLeafValue = LeafValue; + type ProofVerifier = DidMerkleProofVerifier; type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; diff --git a/dip-template/runtimes/dip-receiver/src/lib.rs b/dip-template/runtimes/dip-receiver/src/lib.rs index 6dd9a52193..6f9db47ab0 100644 --- a/dip-template/runtimes/dip-receiver/src/lib.rs +++ b/dip-template/runtimes/dip-receiver/src/lib.rs @@ -79,8 +79,9 @@ pub type Balance = u128; pub type Block = generic::Block; pub type BlockNumber = u32; pub type DidIdentifier = AccountId; +pub type Hasher = BlakeTwo256; pub type Hash = sp_core::H256; -pub type Header = generic::Header; +pub type Header = generic::Header; pub type Index = u32; pub type Signature = MultiSignature; @@ -228,8 +229,8 @@ impl frame_system::Config for Runtime { type BlockWeights = RuntimeBlockWeights; type DbWeight = RocksDbWeight; type Hash = Hash; - type Hashing = BlakeTwo256; - type Header = generic::Header; + type Hashing = Hasher; + type Header = generic::Header; type Index = Index; type Lookup = AccountIdLookup; type MaxConsumers = ConstU32<16>; diff --git a/dip-template/runtimes/dip-sender/Cargo.toml b/dip-template/runtimes/dip-sender/Cargo.toml index 307bb37ff3..fe9e488624 100644 --- a/dip-template/runtimes/dip-sender/Cargo.toml +++ b/dip-template/runtimes/dip-sender/Cargo.toml @@ -21,6 +21,7 @@ scale-info = {workspace = true, features = ["derive"]} did.workspace = true dip-support.workspace = true pallet-dip-sender.workspace = true +runtime-common.workspace = true # Substrate frame-executive.workspace = true @@ -75,6 +76,7 @@ std = [ "did/std", "dip-support/std", "pallet-dip-sender/std", + "runtime-common/std", "frame-executive/std", "frame-support/std", "frame-system/std", diff --git a/dip-template/runtimes/dip-sender/src/dip.rs b/dip-template/runtimes/dip-sender/src/dip.rs index 2d43c62db4..ad05b3f0ce 100644 --- a/dip-template/runtimes/dip-sender/src/dip.rs +++ b/dip-template/runtimes/dip-sender/src/dip.rs @@ -17,13 +17,13 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use codec::{Decode, Encode}; +use did::did_details::DidDetails; use dip_support::VersionedIdentityProofAction; -use pallet_dip_sender::traits::{ - DefaultIdentityProofGenerator, DefaultIdentityProvider, TxBuilder, XcmRouterDispatcher, -}; +use pallet_dip_sender::traits::{TxBuilder, XcmRouterDispatcher}; +use runtime_common::dip::sender::{DidIdentityProvider, DidMerkleRootGenerator}; use xcm::{latest::MultiLocation, DoubleEncoded}; -use crate::{DidIdentifier, Runtime, RuntimeEvent, XcmRouter}; +use crate::{DidIdentifier, Hash, Runtime, RuntimeEvent, XcmRouter}; #[derive(Encode, Decode)] enum ReceiverParachainCalls { @@ -34,16 +34,16 @@ enum ReceiverParachainCalls { #[derive(Encode, Decode)] enum ReceiverParachainDipReceiverCalls { #[codec(index = 0)] - ProcessIdentityAction(VersionedIdentityProofAction), + ProcessIdentityAction(VersionedIdentityProofAction), } pub struct ReceiverParachainTxBuilder; -impl TxBuilder for ReceiverParachainTxBuilder { +impl TxBuilder for ReceiverParachainTxBuilder { type Error = (); fn build( _dest: MultiLocation, - action: VersionedIdentityProofAction, + action: VersionedIdentityProofAction, ) -> Result, Self::Error> { let double_encoded: DoubleEncoded<()> = ReceiverParachainCalls::DipReceiver(ReceiverParachainDipReceiverCalls::ProcessIdentityAction(action)) @@ -55,16 +55,11 @@ impl TxBuilder for ReceiverParachainTxBuilder { impl pallet_dip_sender::Config for Runtime { type Identifier = DidIdentifier; - // TODO: Change with right one - type Identity = u32; - // TODO: Change with right one - type IdentityProofDispatcher = XcmRouterDispatcher; - // TODO: Change with right one - type IdentityProofGenerator = DefaultIdentityProofGenerator; - // TODO: Change with right one - type IdentityProvider = DefaultIdentityProvider; - // TODO: Change with right one - type ProofOutput = [u8; 32]; + type Identity = DidDetails; + type IdentityProofDispatcher = XcmRouterDispatcher; + type IdentityProofGenerator = DidMerkleRootGenerator; + type IdentityProvider = DidIdentityProvider; + type ProofOutput = Hash; type RuntimeEvent = RuntimeEvent; type TxBuilder = ReceiverParachainTxBuilder; } diff --git a/pallets/pallet-dip-sender/src/lib.rs b/pallets/pallet-dip-sender/src/lib.rs index 57d3c38fa0..4af958ccd8 100644 --- a/pallets/pallet-dip-sender/src/lib.rs +++ b/pallets/pallet-dip-sender/src/lib.rs @@ -48,7 +48,11 @@ pub mod pallet { type Identifier: Parameter; type Identity; type ProofOutput: Clone + Eq + Debug; - type IdentityProofGenerator: IdentityProofGenerator; + type IdentityProofGenerator: IdentityProofGenerator< + Self::Identifier, + Self::Identity, + Output = Self::ProofOutput, + >; type IdentityProofDispatcher: IdentityProofDispatcher; type IdentityProvider: IdentityProvider; type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -98,7 +102,7 @@ pub mod pallet { let destination: MultiLocation = (*destination).try_into().map_err(|_| Error::::BadVersion)?; let action: IdentityProofActionOf = match T::IdentityProvider::retrieve(&identifier) { Ok(Some((identity, _))) => { - let identity_proof = T::IdentityProofGenerator::generate_proof(&identifier, &identity) + let identity_proof = T::IdentityProofGenerator::generate_commitment(&identifier, &identity) .map_err(|_| Error::::IdentityProofGeneration)?; Ok(IdentityProofAction::Updated(identifier, identity_proof, ())) } diff --git a/pallets/pallet-dip-sender/src/traits.rs b/pallets/pallet-dip-sender/src/traits.rs index 63209fd8d9..a9f931c3f1 100644 --- a/pallets/pallet-dip-sender/src/traits.rs +++ b/pallets/pallet-dip-sender/src/traits.rs @@ -21,25 +21,28 @@ use xcm::{latest::prelude::*, DoubleEncoded}; pub use identity_generation::*; pub mod identity_generation { + use sp_std::marker::PhantomData; - pub trait IdentityProofGenerator { + pub trait IdentityProofGenerator { type Error; + type Output; - fn generate_proof(identifier: &Identifier, identity: &Identity) -> Result; + fn generate_commitment(identifier: &Identifier, identity: &Identity) -> Result; } // Implement the `IdentityProofGenerator` by returning the `Default` value for // the `Output` type. - pub struct DefaultIdentityProofGenerator; + pub struct DefaultIdentityProofGenerator(PhantomData); - impl IdentityProofGenerator - for DefaultIdentityProofGenerator + impl IdentityProofGenerator + for DefaultIdentityProofGenerator where Output: Default, { type Error = (); + type Output = Output; - fn generate_proof(_identifier: &Identifier, _identity: &Identity) -> Result { + fn generate_commitment(_identifier: &Identifier, _identity: &Identity) -> Result { Ok(Output::default()) } } diff --git a/runtimes/common/src/dip.rs b/runtimes/common/src/dip.rs index 01765be469..6b8537dea3 100644 --- a/runtimes/common/src/dip.rs +++ b/runtimes/common/src/dip.rs @@ -16,79 +16,89 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use sp_std::marker::PhantomData; +use codec::{Decode, Encode, MaxEncodedLen}; +use did::{did_details::DidPublicKeyDetails, DidVerificationKeyRelationship}; +use scale_info::TypeInfo; +use sp_std::{marker::PhantomData, vec::Vec}; -pub mod sender { - use super::*; - - use codec::{Decode, Encode}; - use did::{ - did_details::{DidDetails, DidPublicKeyDetails}, - DidVerificationKeyRelationship, KeyIdOf, - }; - use pallet_dip_sender::traits::{IdentityProofGenerator, IdentityProvider}; +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo)] +pub enum KeyRelationship { + Encryption, + Verification(DidVerificationKeyRelationship), +} - pub enum MerkleLeaf { - VerificationKey(KeyIdOf, DidVerificationKeyRelationship), - EncryptionKey(KeyIdOf), - PublicKey(KeyIdOf, DidPublicKeyDetails), +impl From for KeyRelationship { + fn from(value: DidVerificationKeyRelationship) -> Self { + Self::Verification(value) } +} - impl MerkleLeaf - where - T: did::Config, - { - pub fn key(&self) -> Vec { - match self { - MerkleLeaf::VerificationKey(_, relationship) => relationship.encode(), - MerkleLeaf::EncryptionKey(key_id) => key_id.encode(), - MerkleLeaf::PublicKey(key_id, _) => key_id.encode(), - } - } +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo)] +pub enum LeafKey { + KeyId(KeyId), + KeyRelationship(KeyRelationship), +} - pub fn value(&self) -> Vec { - match self { - MerkleLeaf::VerificationKey(key_id, relationship) => key_id.encode(), - MerkleLeaf::EncryptionKey(key_id) => key_id.encode(), - MerkleLeaf::PublicKey(_, public_key) => public_key.encode(), - } - } - } +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo)] +pub enum LeafValue { + KeyId(KeyId), + KeyDetails(DidPublicKeyDetails), +} - impl TryFrom<(Vec, Vec)> for MerkleLeaf - where - T: did::Config, - { - // TODO: Proper error handling - type Error = (); +struct MerkleLeaf { + pub key: LeafKey, + pub value: LeafValue, +} - fn try_from((key, value): (Vec, Vec)) -> Result { - let (key, value) = (&mut key.as_slice(), &mut value.as_slice()); - if let (Ok(relationship), Ok(key_id)) = - (DidVerificationKeyRelationship::decode(key), KeyIdOf::::decode(value)) - { - Ok(Self::VerificationKey(key_id, relationship)) - } else if let (Ok(key_id), Ok(_)) = (KeyIdOf::::decode(key), KeyIdOf::::decode(value)) { - Ok(Self::EncryptionKey(key_id)) - } else if let (Ok(key_id), Ok(public_key)) = (KeyIdOf::::decode(key), DidPublicKeyDetails::decode(value)) - { - Ok(Self::PublicKey(key_id, public_key)) - } else { - Err(()) - } +// TODO: Generalize Vec input to the minimum capabilities needed for this, +// so as to avoid unnecessary allocations and/or copies. +impl TryFrom<(Vec, Vec)> for MerkleLeaf +where + KeyId: Decode, + BlockNumber: Decode + MaxEncodedLen, +{ + // TODO: Proper error handling + type Error = (); + + fn try_from((key, value): (Vec, Vec)) -> Result { + let (key, value) = (&mut key.as_slice(), &mut value.as_slice()); + let decoded_key = LeafKey::decode(key).map_err(|_| ())?; + let decoded_value = LeafValue::decode(value).map_err(|_| ())?; + match (&decoded_key, &decoded_value) { + (LeafKey::KeyId(_), LeafValue::KeyDetails(_)) => Ok(Self { + key: decoded_key, + value: decoded_value, + }), + (LeafKey::KeyRelationship(_), LeafValue::KeyId(_)) => Ok(Self { + key: decoded_key, + value: decoded_value, + }), + _ => Err(()), } } +} + +pub mod sender { + use super::*; + + use did::did_details::DidDetails; + use pallet_dip_sender::traits::{IdentityProofGenerator, IdentityProvider}; + use sp_std::borrow::ToOwned; pub struct DidMerkleRootGenerator(PhantomData); - impl IdentityProofGenerator, T::Hash> for DidMerkleRootGenerator + impl IdentityProofGenerator> for DidMerkleRootGenerator where T: did::Config, { // TODO: Proper error handling type Error = (); + type Output = T::Hash; - fn generate_proof(_identifier: &T::DidIdentifier, identity: &DidDetails) -> Result { + fn generate_commitment( + _identifier: &T::DidIdentifier, + identity: &DidDetails, + ) -> Result { use sp_trie::{LayoutV1, MemoryDB, TrieDBMutBuilder, TrieHash, TrieMut}; let mut db = MemoryDB::default(); @@ -96,48 +106,55 @@ pub mod sender { let mut trie_builder = TrieDBMutBuilder::>::new(&mut db, &mut trie).build(); // Authentication key - let auth_key_leaf = MerkleLeaf::::VerificationKey( - identity.authentication_key, - DidVerificationKeyRelationship::Authentication, - ); + let auth_key_leaf = MerkleLeaf { + key: LeafKey::KeyRelationship(DidVerificationKeyRelationship::Authentication.into()), + value: LeafValue::<_, T::BlockNumber>::KeyId(identity.authentication_key), + }; trie_builder .insert( - auth_key_leaf.key().encode().as_slice(), - auth_key_leaf.value().encode().as_slice(), + auth_key_leaf.key.encode().as_slice(), + auth_key_leaf.value.encode().as_slice(), ) .map_err(|_| ())?; // Attestation key: (key relationship, key id) if let Some(att_key_id) = identity.attestation_key { - let att_key_leaf = - MerkleLeaf::::VerificationKey(att_key_id, DidVerificationKeyRelationship::AssertionMethod); + let att_key_leaf = MerkleLeaf { + key: LeafKey::KeyRelationship(DidVerificationKeyRelationship::AssertionMethod.into()), + value: LeafValue::<_, T::BlockNumber>::KeyId(att_key_id), + }; trie_builder .insert( - att_key_leaf.key().encode().as_slice(), - att_key_leaf.value().encode().as_slice(), + att_key_leaf.key.encode().as_slice(), + att_key_leaf.value.encode().as_slice(), ) .map_err(|_| ())?; }; // Delegation key: (key relationship, key id) if let Some(del_key_id) = identity.delegation_key { - let del_key_leaf = - MerkleLeaf::::VerificationKey(del_key_id, DidVerificationKeyRelationship::CapabilityDelegation); + let del_key_leaf = MerkleLeaf { + key: LeafKey::KeyRelationship(DidVerificationKeyRelationship::CapabilityDelegation.into()), + value: LeafValue::<_, T::BlockNumber>::KeyId(del_key_id), + }; trie_builder .insert( - del_key_leaf.key().encode().as_slice(), - del_key_leaf.value().encode().as_slice(), + del_key_leaf.key.encode().as_slice(), + del_key_leaf.value.encode().as_slice(), ) .map_err(|_| ())?; }; // Key agreement keys [(enc-, key id)] identity .key_agreement_keys - .into_iter() + .iter() .try_for_each(|id| -> Result<(), ()> { - let enc_key_leaf = MerkleLeaf::::EncryptionKey(id); + let enc_key_leaf = MerkleLeaf { + key: LeafKey::KeyRelationship(KeyRelationship::Encryption), + value: LeafValue::<_, T::BlockNumber>::KeyId(id), + }; trie_builder .insert( - enc_key_leaf.key().encode().as_slice(), - enc_key_leaf.value().encode().as_slice(), + enc_key_leaf.key.encode().as_slice(), + enc_key_leaf.value.encode().as_slice(), ) .map_err(|_| ())?; Ok(()) @@ -145,13 +162,16 @@ pub mod sender { // Public keys: [(key id, public key)] identity .public_keys - .into_iter() + .iter() .try_for_each(|(id, key_details)| -> Result<(), ()> { - let pub_key_leaf = MerkleLeaf::::PublicKey(id, key_details); + let pub_key_leaf = MerkleLeaf { + key: LeafKey::KeyId(id), + value: LeafValue::KeyDetails(key_details.clone()), + }; trie_builder .insert( - pub_key_leaf.key().encode().as_slice(), - pub_key_leaf.value().encode().as_slice(), + pub_key_leaf.key.encode().as_slice(), + pub_key_leaf.value.encode().as_slice(), ) .map_err(|_| ())?; Ok(()) @@ -184,36 +204,44 @@ pub mod sender { } pub mod receiver { - use crate::dip::sender::MerkleLeaf; - use super::*; - use did::{did_details::DidPublicKey, DidVerificationKeyRelationship}; use dip_support::VersionedIdentityProof; use pallet_dip_receiver::traits::IdentityProofVerifier; - use sp_std::vec::Vec; + use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; use sp_trie::LayoutV1; - pub struct ProofEntry { - key: DidPublicKey, - verification_relationship: DidVerificationKeyRelationship, + pub struct ProofEntry { + pub key: DidPublicKeyDetails, + pub relationship: KeyRelationship, } - pub struct VerificationResult(Vec); + pub struct VerificationResult(pub Vec>); - pub struct DidMerkleProofVerifier(PhantomData); + impl From>> for VerificationResult + where + BlockNumber: MaxEncodedLen, + { + fn from(value: Vec>) -> Self { + Self(value) + } + } + + pub struct DidMerkleProofVerifier(PhantomData<(KeyId, BlockNumber, Hasher)>); - impl IdentityProofVerifier for DidMerkleProofVerifier + impl IdentityProofVerifier for DidMerkleProofVerifier where - T: did::Config, + KeyId: MaxEncodedLen + Clone + Ord, + BlockNumber: MaxEncodedLen + Clone + Ord, + Hasher: sp_core::Hasher, { - // TODO: Proper error handling type BlindedValue = Vec>; + // TODO: Proper error handling type Error = (); - type LeafKey = Vec; - type LeafValue = Vec; - type ProofDigest = T::Hash; - type VerificationResult = VerificationResult; + type LeafKey = LeafKey; + type LeafValue = LeafValue; + type ProofDigest = ::Out; + type VerificationResult = VerificationResult; fn verify_proof_against_digest( proof: VersionedIdentityProof, @@ -223,27 +251,53 @@ pub mod receiver { use sp_trie::verify_trie_proof; let proof: v1::Proof<_, _, _> = proof.try_into()?; - verify_trie_proof::, _, _, _>( - &digest, - &proof.blinded, - proof.revealed.iter().map(|(key, value)| (key, Some(value))).collect(), - ) - .map_err(|_| ())?; - - let revealed_leaves: Vec> = proof + // 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(|(key, value)| MerkleLeaf::try_from((*key, *value)).expect("Error.")) + .map(|(key, value)| (key.encode(), Some(value.encode()))) + .collect::, Option>)>>(); + verify_trie_proof::, _, _, _>(&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. + + // 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(|(key, value)| { + if let (LeafKey::KeyId(key_id), LeafValue::KeyDetails(key_details)) = (key, value) { + Some((key_id, key_details)) + } else { + None + } + }) + .collect(); + // Create a list of the revealed verification keys + let verification_keys: Vec> = proof + .revealed + .into_iter() + .filter_map(|(key, value)| { + if let (LeafKey::KeyRelationship(key_rel), LeafValue::KeyId(key_id)) = (key, value) { + // 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_rel, + }) + } else { + None + } + }) .collect(); - // .map(|(key, value)| match key.as_ref() { - // AUTH_KEY_LEAF => ( - // KeyIdOf::::from(key.as_slice()), - // DidVerificationKeyRelationship::Authentication, - // ), - // ATT_KEY_LEAF => (*key, - // DidVerificationKeyRelationship::AssertionMethod), DEL_KEY_LEAF => - // (*key, DidVerificationKeyRelationship::CapabilityDelegation), }) - // .collect(); + Ok(verification_keys.into()) } } } From 67cd0aeee3a1f11d08a0e0be22da0078bdce5065 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 22 Mar 2023 16:53:22 +0100 Subject: [PATCH 04/23] Wip for the proof generator function --- runtimes/common/src/dip.rs | 63 +++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/runtimes/common/src/dip.rs b/runtimes/common/src/dip.rs index 6b8537dea3..ce92370c2f 100644 --- a/runtimes/common/src/dip.rs +++ b/runtimes/common/src/dip.rs @@ -21,7 +21,7 @@ use did::{did_details::DidPublicKeyDetails, DidVerificationKeyRelationship}; use scale_info::TypeInfo; use sp_std::{marker::PhantomData, vec::Vec}; -#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo)] +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord)] pub enum KeyRelationship { Encryption, Verification(DidVerificationKeyRelationship), @@ -33,7 +33,7 @@ impl From for KeyRelationship { } } -#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo)] +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord)] pub enum LeafKey { KeyId(KeyId), KeyRelationship(KeyRelationship), @@ -81,12 +81,67 @@ where pub mod sender { use super::*; - use did::did_details::DidDetails; + use did::{did_details::DidDetails, KeyIdOf}; + use dip_support::latest::Proof; use pallet_dip_sender::traits::{IdentityProofGenerator, IdentityProvider}; - use sp_std::borrow::ToOwned; + use sp_std::{ + borrow::ToOwned, + collections::{btree_map::BTreeMap, btree_set::BTreeSet}, + }; pub struct DidMerkleRootGenerator(PhantomData); + pub type DidMerkleProof = + Proof>, LeafKey>, LeafValue, ::BlockNumber>>; + + impl DidMerkleRootGenerator + where + T: did::Config, + { + // TODO: Better error handling + pub fn generate_proof(details: &DidDetails, key_ids: BTreeSet>) -> Result, ()> { + use sp_trie::generate_trie_proof; + + let mut leaves: BTreeMap>, LeafValue, T::BlockNumber>>; + + key_ids.iter().try_for_each(|key_id| { + let key_details = details.public_keys.get(key_id).ok_or(())?; + if *key_id == details.authentication_key { + leaves.insert( + LeafKey::KeyRelationship(DidVerificationKeyRelationship::Authentication.into()), + LeafValue::KeyId(*key_id), + ); + Ok(()) + } else if let Some(key_id) = details.attestation_key { + leaves.insert( + LeafKey::KeyRelationship(DidVerificationKeyRelationship::AssertionMethod.into()), + LeafValue::KeyId(key_id), + ); + Ok(()) + } else if let Some(key_id) = details.delegation_key { + leaves.insert( + LeafKey::KeyRelationship(DidVerificationKeyRelationship::CapabilityDelegation.into()), + LeafValue::KeyId(key_id), + ); + Ok(()) + } else if details.key_agreement_keys.contains(key_id) { + leaves.insert( + LeafKey::KeyRelationship(KeyRelationship::Encryption), + LeafValue::KeyId(*key_id), + ); + Ok(()) + } else { + Err(()) + }?; + leaves.insert(LeafKey::KeyId(*key_id), LeafValue::KeyDetails(*key_details)); + Ok::<_, ()>(()) + })?; + generate_trie_proof(db, root, keys) + // TODO: Call calculate_root_with_db after creating the function + unimplemented!() + } + } + impl IdentityProofGenerator> for DidMerkleRootGenerator where T: did::Config, From 9d19e34a1c6209e89c331482e95b875fb982a985 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Thu, 23 Mar 2023 11:37:36 +0100 Subject: [PATCH 05/23] Test works! --- pallets/did/src/did_details.rs | 2 +- runtimes/common/src/dip.rs | 438 ++++++++++++++++++++++----------- 2 files changed, 299 insertions(+), 141 deletions(-) diff --git a/pallets/did/src/did_details.rs b/pallets/did/src/did_details.rs index c0437277e0..6d9c2cdb0f 100644 --- a/pallets/did/src/did_details.rs +++ b/pallets/did/src/did_details.rs @@ -117,7 +117,7 @@ impl From for DidPublicKey { /// Verification methods a verification key can /// fulfil, according to the [DID specification](https://w3c.github.io/did-spec-registries/#verification-relationships). -#[derive(Clone, Copy, RuntimeDebug, Decode, Encode, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +#[derive(Clone, Copy, RuntimeDebug, Decode, Encode, PartialEq, Eq, TypeInfo, MaxEncodedLen, PartialOrd, Ord)] pub enum DidVerificationKeyRelationship { /// Key used to authenticate all the DID operations. Authentication, diff --git a/runtimes/common/src/dip.rs b/runtimes/common/src/dip.rs index ce92370c2f..d644fd2de0 100644 --- a/runtimes/common/src/dip.rs +++ b/runtimes/common/src/dip.rs @@ -18,10 +18,11 @@ use codec::{Decode, Encode, MaxEncodedLen}; use did::{did_details::DidPublicKeyDetails, DidVerificationKeyRelationship}; +use frame_support::RuntimeDebug; use scale_info::TypeInfo; use sp_std::{marker::PhantomData, vec::Vec}; -#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord)] +#[derive(Clone, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord)] pub enum KeyRelationship { Encryption, Verification(DidVerificationKeyRelationship), @@ -33,51 +34,18 @@ impl From for KeyRelationship { } } -#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord)] +#[derive(Clone, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord)] pub enum LeafKey { - KeyId(KeyId), - KeyRelationship(KeyRelationship), + KeyReference(KeyId, KeyRelationship), + KeyDetails(KeyId), } -#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo)] -pub enum LeafValue { - KeyId(KeyId), +#[derive(Clone, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo)] +pub enum LeafValue { + KeyReference, KeyDetails(DidPublicKeyDetails), } -struct MerkleLeaf { - pub key: LeafKey, - pub value: LeafValue, -} - -// TODO: Generalize Vec input to the minimum capabilities needed for this, -// so as to avoid unnecessary allocations and/or copies. -impl TryFrom<(Vec, Vec)> for MerkleLeaf -where - KeyId: Decode, - BlockNumber: Decode + MaxEncodedLen, -{ - // TODO: Proper error handling - type Error = (); - - fn try_from((key, value): (Vec, Vec)) -> Result { - let (key, value) = (&mut key.as_slice(), &mut value.as_slice()); - let decoded_key = LeafKey::decode(key).map_err(|_| ())?; - let decoded_value = LeafValue::decode(value).map_err(|_| ())?; - match (&decoded_key, &decoded_value) { - (LeafKey::KeyId(_), LeafValue::KeyDetails(_)) => Ok(Self { - key: decoded_key, - value: decoded_value, - }), - (LeafKey::KeyRelationship(_), LeafValue::KeyId(_)) => Ok(Self { - key: decoded_key, - value: decoded_value, - }), - _ => Err(()), - } - } -} - pub mod sender { use super::*; @@ -88,145 +56,81 @@ pub mod sender { borrow::ToOwned, collections::{btree_map::BTreeMap, btree_set::BTreeSet}, }; + use sp_trie::{LayoutV1, MemoryDB}; pub struct DidMerkleRootGenerator(PhantomData); pub type DidMerkleProof = - Proof>, LeafKey>, LeafValue, ::BlockNumber>>; + Proof>, LeafKey>, LeafValue<::BlockNumber>>; impl DidMerkleRootGenerator where T: did::Config, { - // TODO: Better error handling - pub fn generate_proof(details: &DidDetails, key_ids: BTreeSet>) -> Result, ()> { - use sp_trie::generate_trie_proof; - - let mut leaves: BTreeMap>, LeafValue, T::BlockNumber>>; - - key_ids.iter().try_for_each(|key_id| { - let key_details = details.public_keys.get(key_id).ok_or(())?; - if *key_id == details.authentication_key { - leaves.insert( - LeafKey::KeyRelationship(DidVerificationKeyRelationship::Authentication.into()), - LeafValue::KeyId(*key_id), - ); - Ok(()) - } else if let Some(key_id) = details.attestation_key { - leaves.insert( - LeafKey::KeyRelationship(DidVerificationKeyRelationship::AssertionMethod.into()), - LeafValue::KeyId(key_id), - ); - Ok(()) - } else if let Some(key_id) = details.delegation_key { - leaves.insert( - LeafKey::KeyRelationship(DidVerificationKeyRelationship::CapabilityDelegation.into()), - LeafValue::KeyId(key_id), - ); - Ok(()) - } else if details.key_agreement_keys.contains(key_id) { - leaves.insert( - LeafKey::KeyRelationship(KeyRelationship::Encryption), - LeafValue::KeyId(*key_id), - ); - Ok(()) - } else { - Err(()) - }?; - leaves.insert(LeafKey::KeyId(*key_id), LeafValue::KeyDetails(*key_details)); - Ok::<_, ()>(()) - })?; - generate_trie_proof(db, root, keys) - // TODO: Call calculate_root_with_db after creating the function - unimplemented!() - } - } + fn calculate_root_with_db(identity: &DidDetails, db: &mut MemoryDB) -> Result { + use sp_trie::{TrieDBMutBuilder, TrieHash, TrieMut}; - impl IdentityProofGenerator> for DidMerkleRootGenerator - where - T: did::Config, - { - // TODO: Proper error handling - type Error = (); - type Output = T::Hash; - - fn generate_commitment( - _identifier: &T::DidIdentifier, - identity: &DidDetails, - ) -> Result { - use sp_trie::{LayoutV1, MemoryDB, TrieDBMutBuilder, TrieHash, TrieMut}; - - let mut db = MemoryDB::default(); let mut trie = TrieHash::>::default(); - let mut trie_builder = TrieDBMutBuilder::>::new(&mut db, &mut trie).build(); + let mut trie_builder = TrieDBMutBuilder::>::new(db, &mut trie).build(); // Authentication key - let auth_key_leaf = MerkleLeaf { - key: LeafKey::KeyRelationship(DidVerificationKeyRelationship::Authentication.into()), - value: LeafValue::<_, T::BlockNumber>::KeyId(identity.authentication_key), - }; trie_builder .insert( - auth_key_leaf.key.encode().as_slice(), - auth_key_leaf.value.encode().as_slice(), + LeafKey::KeyReference( + identity.authentication_key, + DidVerificationKeyRelationship::Authentication.into(), + ) + .encode() + .as_slice(), + LeafValue::::KeyReference.encode().as_slice(), ) .map_err(|_| ())?; - // Attestation key: (key relationship, key id) + // Attestation key if let Some(att_key_id) = identity.attestation_key { - let att_key_leaf = MerkleLeaf { - key: LeafKey::KeyRelationship(DidVerificationKeyRelationship::AssertionMethod.into()), - value: LeafValue::<_, T::BlockNumber>::KeyId(att_key_id), - }; trie_builder .insert( - att_key_leaf.key.encode().as_slice(), - att_key_leaf.value.encode().as_slice(), + LeafKey::KeyReference(att_key_id, DidVerificationKeyRelationship::AssertionMethod.into()) + .encode() + .as_slice(), + LeafValue::::KeyReference.encode().as_slice(), ) .map_err(|_| ())?; }; - // Delegation key: (key relationship, key id) + // Delegation key if let Some(del_key_id) = identity.delegation_key { - let del_key_leaf = MerkleLeaf { - key: LeafKey::KeyRelationship(DidVerificationKeyRelationship::CapabilityDelegation.into()), - value: LeafValue::<_, T::BlockNumber>::KeyId(del_key_id), - }; trie_builder .insert( - del_key_leaf.key.encode().as_slice(), - del_key_leaf.value.encode().as_slice(), + LeafKey::KeyReference(del_key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()) + .encode() + .as_slice(), + LeafValue::::KeyReference.encode().as_slice(), ) .map_err(|_| ())?; }; - // Key agreement keys [(enc-, key id)] + // Key agreement keys identity .key_agreement_keys .iter() .try_for_each(|id| -> Result<(), ()> { - let enc_key_leaf = MerkleLeaf { - key: LeafKey::KeyRelationship(KeyRelationship::Encryption), - value: LeafValue::<_, T::BlockNumber>::KeyId(id), - }; trie_builder .insert( - enc_key_leaf.key.encode().as_slice(), - enc_key_leaf.value.encode().as_slice(), + LeafKey::KeyReference(*id, KeyRelationship::Encryption) + .encode() + .as_slice(), + LeafValue::::KeyReference.encode().as_slice(), ) .map_err(|_| ())?; Ok(()) })?; - // Public keys: [(key id, public key)] + // Public keys identity .public_keys .iter() .try_for_each(|(id, key_details)| -> Result<(), ()> { - let pub_key_leaf = MerkleLeaf { - key: LeafKey::KeyId(id), - value: LeafValue::KeyDetails(key_details.clone()), - }; trie_builder .insert( - pub_key_leaf.key.encode().as_slice(), - pub_key_leaf.value.encode().as_slice(), + LeafKey::KeyDetails(*id).encode().as_slice(), + LeafValue::KeyDetails(key_details.clone()).encode().as_slice(), ) .map_err(|_| ())?; Ok(()) @@ -234,6 +138,80 @@ pub mod sender { trie_builder.commit(); Ok(trie_builder.root().to_owned()) } + + // TODO: Better error handling + #[allow(clippy::result_unit_err)] + pub fn generate_proof( + identity: &DidDetails, + key_ids: BTreeSet>, + ) -> Result<(T::Hash, DidMerkleProof), ()> { + use sp_trie::generate_trie_proof; + + let mut db = MemoryDB::default(); + let root = Self::calculate_root_with_db(identity, &mut db)?; + + #[allow(clippy::type_complexity)] + let mut leaves: BTreeMap>, LeafValue> = BTreeMap::new(); + key_ids.iter().try_for_each(|key_id| { + let key_details = identity.public_keys.get(key_id).ok_or(())?; + if *key_id == identity.authentication_key { + leaves.insert( + LeafKey::KeyReference(*key_id, DidVerificationKeyRelationship::Authentication.into()), + LeafValue::KeyReference, + ); + Ok(()) + } else if let Some(key_id) = identity.attestation_key { + leaves.insert( + LeafKey::KeyReference(key_id, DidVerificationKeyRelationship::AssertionMethod.into()), + LeafValue::KeyReference, + ); + Ok(()) + } else if let Some(key_id) = identity.delegation_key { + leaves.insert( + LeafKey::KeyReference(key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()), + LeafValue::KeyReference, + ); + Ok(()) + } else if identity.key_agreement_keys.contains(key_id) { + leaves.insert( + LeafKey::KeyReference(*key_id, KeyRelationship::Encryption), + LeafValue::KeyReference, + ); + Ok(()) + } else { + Err(()) + }?; + leaves.insert(LeafKey::KeyDetails(*key_id), LeafValue::KeyDetails(key_details.clone())); + Ok::<_, ()>(()) + })?; + let encoded_keys: Vec> = leaves.keys().map(|k| k.encode()).collect(); + let proof = + generate_trie_proof::, _, _, _>(&db, root, &encoded_keys).map_err(|_| ())?; + Ok(( + root, + DidMerkleProof:: { + blinded: proof, + revealed: leaves.into_iter().collect::>(), + }, + )) + } + } + + impl IdentityProofGenerator> for DidMerkleRootGenerator + where + T: did::Config, + { + // TODO: Proper error handling + type Error = (); + type Output = T::Hash; + + fn generate_commitment( + _identifier: &T::DidIdentifier, + identity: &DidDetails, + ) -> Result { + let mut db = MemoryDB::default(); + Self::calculate_root_with_db(identity, &mut db) + } } pub struct DidIdentityProvider(PhantomData); @@ -266,11 +244,13 @@ pub mod receiver { use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; use sp_trie::LayoutV1; + #[derive(RuntimeDebug)] pub struct ProofEntry { pub key: DidPublicKeyDetails, pub relationship: KeyRelationship, } + #[derive(RuntimeDebug)] pub struct VerificationResult(pub Vec>); impl From>> for VerificationResult @@ -294,7 +274,7 @@ pub mod receiver { // TODO: Proper error handling type Error = (); type LeafKey = LeafKey; - type LeafValue = LeafValue; + type LeafValue = LeafValue; type ProofDigest = ::Out; type VerificationResult = VerificationResult; @@ -326,19 +306,19 @@ pub mod receiver { .clone() .into_iter() .filter_map(|(key, value)| { - if let (LeafKey::KeyId(key_id), LeafValue::KeyDetails(key_details)) = (key, value) { + if let (LeafKey::KeyDetails(key_id), LeafValue::KeyDetails(key_details)) = (key, value) { Some((key_id, key_details)) } else { None } }) .collect(); - // Create a list of the revealed verification keys - let verification_keys: Vec> = proof + // Create a list of the revealed keys + let keys: Vec> = proof .revealed .into_iter() .filter_map(|(key, value)| { - if let (LeafKey::KeyRelationship(key_rel), LeafValue::KeyId(key_id)) = (key, value) { + if let (LeafKey::KeyReference(key_id, key_rel), LeafValue::KeyReference) = (key, value) { // TODO: Better error handling. let key_details = public_keys .get(&key_id) @@ -352,7 +332,185 @@ pub mod receiver { } }) .collect(); - Ok(verification_keys.into()) + Ok(keys.into()) } } } + +#[cfg(test)] +mod test { + use crate::dip::{receiver::DidMerkleProofVerifier, sender::DidMerkleRootGenerator}; + + use super::*; + + use did::{did_details::DidCreationDetails, KeyIdOf}; + use frame_support::{ + assert_ok, construct_runtime, parameter_types, traits::Everything, weights::constants::RocksDbWeight, + }; + use frame_system::{ + mocking::{MockBlock, MockUncheckedExtrinsic}, + EnsureSigned, RawOrigin, + }; + use pallet_dip_receiver::traits::IdentityProofVerifier; + use sp_core::{ed25519, 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; + + 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<1>; + 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 authentication_merkle_proof_works() { + 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![], + }; + 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"); + let (root, proof) = DidMerkleRootGenerator::::generate_proof( + &did_details, + BTreeSet::from_iter(vec![did_details.authentication_key]), + ) + .expect("Merkle proof generation should not fail."); + println!("{:?} - {:?}", root, proof); + assert_ok!( + DidMerkleProofVerifier::, BlockNumber, Hashing>::verify_proof_against_digest( + proof.into(), + root + ) + ); + }) + } +} From 6180880b274f53981d14c02474cf96090362e5e2 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Thu, 23 Mar 2023 16:41:26 +0100 Subject: [PATCH 06/23] Complete DID Document Merkle proof failing --- crates/dip/src/lib.rs | 16 +- crates/dip/src/v1.rs | 4 +- pallets/pallet-dip-receiver/src/lib.rs | 8 +- pallets/pallet-dip-receiver/src/traits.rs | 18 +- runtimes/common/src/dip.rs | 343 +++++++++++++++------- 5 files changed, 257 insertions(+), 132 deletions(-) diff --git a/crates/dip/src/lib.rs b/crates/dip/src/lib.rs index db9c321c7e..a9b83ac39c 100644 --- a/crates/dip/src/lib.rs +++ b/crates/dip/src/lib.rs @@ -45,26 +45,22 @@ impl From { +pub enum VersionedIdentityProof { #[codec(index = 1)] - V1(v1::Proof), + V1(v1::Proof), } -impl From> - for VersionedIdentityProof -{ - fn from(value: v1::Proof) -> Self { +impl From> for VersionedIdentityProof { + fn from(value: v1::Proof) -> Self { Self::V1(value) } } -impl TryFrom> - for v1::Proof -{ +impl TryFrom> for v1::Proof { // Proper error handling type Error = (); - fn try_from(value: VersionedIdentityProof) -> Result { + fn try_from(value: VersionedIdentityProof) -> Result { #[allow(irrefutable_let_patterns)] if let VersionedIdentityProof::V1(v1::Proof { blinded, revealed }) = value { Ok(Self { blinded, revealed }) diff --git a/crates/dip/src/v1.rs b/crates/dip/src/v1.rs index 0f5e0fc148..7098c72918 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 Proof { pub blinded: BlindedValue, - pub revealed: Vec<(LeafKey, LeafValue)>, + pub revealed: Vec, } diff --git a/pallets/pallet-dip-receiver/src/lib.rs b/pallets/pallet-dip-receiver/src/lib.rs index bb537c3cf3..da20c3c58b 100644 --- a/pallets/pallet-dip-receiver/src/lib.rs +++ b/pallets/pallet-dip-receiver/src/lib.rs @@ -39,7 +39,7 @@ pub mod pallet { use crate::traits::IdentityProofVerifier; pub type VersionedIdentityProofOf = - VersionedIdentityProof<::BlindedValue, ::ProofLeafKey, ::ProofLeafValue>; + VersionedIdentityProof<::BlindedValue, ::ProofLeaf>; const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); @@ -53,13 +53,11 @@ pub mod pallet { pub trait Config: frame_system::Config { type Identifier: Parameter + MaxEncodedLen; type BlindedValue: Parameter; - type ProofLeafKey: Parameter; - type ProofLeafValue: Parameter; + type ProofLeaf: Parameter; type ProofDigest: Parameter + MaxEncodedLen; type ProofVerifier: IdentityProofVerifier< BlindedValue = Self::BlindedValue, - LeafKey = Self::ProofLeafKey, - LeafValue = Self::ProofLeafValue, + ProofLeaf = Self::ProofLeaf, ProofDigest = Self::ProofDigest, >; type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin>; diff --git a/pallets/pallet-dip-receiver/src/traits.rs b/pallets/pallet-dip-receiver/src/traits.rs index 6a514aef76..ef7642fff9 100644 --- a/pallets/pallet-dip-receiver/src/traits.rs +++ b/pallets/pallet-dip-receiver/src/traits.rs @@ -22,33 +22,29 @@ use sp_std::marker::PhantomData; pub trait IdentityProofVerifier { type BlindedValue; type Error; - type LeafKey; - type LeafValue; type ProofDigest; + type ProofLeaf; type VerificationResult; fn verify_proof_against_digest( - proof: VersionedIdentityProof, + proof: VersionedIdentityProof, digest: Self::ProofDigest, ) -> Result; } // Always returns success. -pub struct SuccessfulProofVerifier( - PhantomData<(ProofDigest, LeafKey, LeafValue, BlindedValue)>, -); -impl IdentityProofVerifier - for SuccessfulProofVerifier +pub struct SuccessfulProofVerifier(PhantomData<(ProofDigest, Leaf, BlindedValue)>); +impl IdentityProofVerifier + for SuccessfulProofVerifier { type BlindedValue = BlindedValue; type Error = (); - type LeafKey = LeafKey; - type LeafValue = LeafValue; type ProofDigest = ProofDigest; + type ProofLeaf = Leaf; type VerificationResult = (); fn verify_proof_against_digest( - _proof: VersionedIdentityProof, + _proof: VersionedIdentityProof, _digest: Self::ProofDigest, ) -> Result { Ok(()) diff --git a/runtimes/common/src/dip.rs b/runtimes/common/src/dip.rs index d644fd2de0..b97868ffe5 100644 --- a/runtimes/common/src/dip.rs +++ b/runtimes/common/src/dip.rs @@ -34,16 +34,52 @@ impl From for KeyRelationship { } } -#[derive(Clone, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord)] -pub enum LeafKey { - KeyReference(KeyId, KeyRelationship), - KeyDetails(KeyId), +// #[derive(Clone, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, +// PartialOrd, Ord)] pub enum LeafKey { +// KeyReference(KeyId, KeyRelationship), +// KeyDetails(KeyId), +// } + +// #[derive(Clone, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo)] +// pub enum LeafValue { +// KeyReference, +// KeyDetails(DidPublicKeyDetails), +// } + +#[derive(Clone, Encode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] +pub struct KeyReferenceKey(pub KeyId, pub KeyRelationship); +#[derive(Clone, Encode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] +pub struct KeyReferenceValue; + +#[derive(Clone, Encode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] +pub struct KeyDetailsKey(pub KeyId); +#[derive(Clone, Encode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] +pub struct KeyDetailsValue(pub DidPublicKeyDetails); + +#[derive(Clone, Encode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] +pub enum ProofLeaf { + KeyReference(KeyReferenceKey, KeyReferenceValue), + KeyDetails(KeyDetailsKey, KeyDetailsValue), } -#[derive(Clone, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo)] -pub enum LeafValue { - KeyReference, - KeyDetails(DidPublicKeyDetails), +impl ProofLeaf +where + KeyId: Encode, + BlockNumber: MaxEncodedLen + 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 sender { @@ -52,16 +88,15 @@ pub mod sender { use did::{did_details::DidDetails, KeyIdOf}; use dip_support::latest::Proof; use pallet_dip_sender::traits::{IdentityProofGenerator, IdentityProvider}; - use sp_std::{ - borrow::ToOwned, - collections::{btree_map::BTreeMap, btree_set::BTreeSet}, - }; + use sp_std::borrow::ToOwned; use sp_trie::{LayoutV1, MemoryDB}; - pub struct DidMerkleRootGenerator(PhantomData); + pub type BlindedValue = Vec; pub type DidMerkleProof = - Proof>, LeafKey>, LeafValue<::BlockNumber>>; + Proof, ProofLeaf, ::BlockNumber>>; + + pub struct DidMerkleRootGenerator(PhantomData); impl DidMerkleRootGenerator where @@ -74,37 +109,34 @@ pub mod sender { let mut trie_builder = TrieDBMutBuilder::>::new(db, &mut trie).build(); // Authentication key + let auth_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( + KeyReferenceKey( + identity.authentication_key, + DidVerificationKeyRelationship::Authentication.into(), + ), + KeyReferenceValue, + ); trie_builder - .insert( - LeafKey::KeyReference( - identity.authentication_key, - DidVerificationKeyRelationship::Authentication.into(), - ) - .encode() - .as_slice(), - LeafValue::::KeyReference.encode().as_slice(), - ) + .insert(auth_leaf.encoded_key().as_slice(), auth_leaf.encoded_value().as_slice()) .map_err(|_| ())?; // Attestation key if let Some(att_key_id) = identity.attestation_key { + let att_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( + KeyReferenceKey(att_key_id, DidVerificationKeyRelationship::AssertionMethod.into()), + KeyReferenceValue, + ); trie_builder - .insert( - LeafKey::KeyReference(att_key_id, DidVerificationKeyRelationship::AssertionMethod.into()) - .encode() - .as_slice(), - LeafValue::::KeyReference.encode().as_slice(), - ) + .insert(att_leaf.encoded_key().as_slice(), att_leaf.encoded_value().as_slice()) .map_err(|_| ())?; }; // Delegation key if let Some(del_key_id) = identity.delegation_key { + let del_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( + KeyReferenceKey(del_key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()), + KeyReferenceValue, + ); trie_builder - .insert( - LeafKey::KeyReference(del_key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()) - .encode() - .as_slice(), - LeafValue::::KeyReference.encode().as_slice(), - ) + .insert(del_leaf.encoded_key().as_slice(), del_leaf.encoded_value().as_slice()) .map_err(|_| ())?; }; // Key agreement keys @@ -112,13 +144,12 @@ pub mod sender { .key_agreement_keys .iter() .try_for_each(|id| -> Result<(), ()> { + let enc_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( + KeyReferenceKey(*id, KeyRelationship::Encryption), + KeyReferenceValue, + ); trie_builder - .insert( - LeafKey::KeyReference(*id, KeyRelationship::Encryption) - .encode() - .as_slice(), - LeafValue::::KeyReference.encode().as_slice(), - ) + .insert(enc_leaf.encoded_key().as_slice(), enc_leaf.encoded_value().as_slice()) .map_err(|_| ())?; Ok(()) })?; @@ -127,11 +158,9 @@ pub mod sender { .public_keys .iter() .try_for_each(|(id, key_details)| -> Result<(), ()> { + let key_leaf = ProofLeaf::KeyDetails(KeyDetailsKey(*id), KeyDetailsValue(key_details.clone())); trie_builder - .insert( - LeafKey::KeyDetails(*id).encode().as_slice(), - LeafValue::KeyDetails(key_details.clone()).encode().as_slice(), - ) + .insert(key_leaf.encoded_key().as_slice(), key_leaf.encoded_value().as_slice()) .map_err(|_| ())?; Ok(()) })?; @@ -141,50 +170,56 @@ pub mod sender { // TODO: Better error handling #[allow(clippy::result_unit_err)] - pub fn generate_proof( + pub fn generate_proof<'a, K>( identity: &DidDetails, - key_ids: BTreeSet>, - ) -> Result<(T::Hash, DidMerkleProof), ()> { + mut key_ids: K, + ) -> Result<(T::Hash, DidMerkleProof), ()> + where + K: Iterator>, + { + use sp_std::collections::btree_set::BTreeSet; use sp_trie::generate_trie_proof; let mut db = MemoryDB::default(); let root = Self::calculate_root_with_db(identity, &mut db)?; #[allow(clippy::type_complexity)] - let mut leaves: BTreeMap>, LeafValue> = BTreeMap::new(); - key_ids.iter().try_for_each(|key_id| { - let key_details = identity.public_keys.get(key_id).ok_or(())?; - if *key_id == identity.authentication_key { - leaves.insert( - LeafKey::KeyReference(*key_id, DidVerificationKeyRelationship::Authentication.into()), - LeafValue::KeyReference, - ); - Ok(()) - } else if let Some(key_id) = identity.attestation_key { - leaves.insert( - LeafKey::KeyReference(key_id, DidVerificationKeyRelationship::AssertionMethod.into()), - LeafValue::KeyReference, - ); - Ok(()) - } else if let Some(key_id) = identity.delegation_key { - leaves.insert( - LeafKey::KeyReference(key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()), - LeafValue::KeyReference, - ); - Ok(()) - } else if identity.key_agreement_keys.contains(key_id) { - leaves.insert( - LeafKey::KeyReference(*key_id, KeyRelationship::Encryption), - LeafValue::KeyReference, - ); - Ok(()) - } else { - Err(()) - }?; - leaves.insert(LeafKey::KeyDetails(*key_id), LeafValue::KeyDetails(key_details.clone())); - Ok::<_, ()>(()) - })?; - let encoded_keys: Vec> = leaves.keys().map(|k| k.encode()).collect(); + let leaves: BTreeSet, T::BlockNumber>> = + key_ids.try_fold(BTreeSet::new(), |mut set, key_id| -> Result<_, ()> { + println!("Key ID: {:?}", key_id); + println!("Public keys: {:?}", identity.public_keys); + let key_details = identity.public_keys.get(key_id).ok_or(())?; + if *key_id == identity.authentication_key { + set.insert(ProofLeaf::KeyReference( + KeyReferenceKey(*key_id, DidVerificationKeyRelationship::Authentication.into()), + KeyReferenceValue, + )); + } + if let Some(key_id) = identity.attestation_key { + set.insert(ProofLeaf::KeyReference( + KeyReferenceKey(key_id, DidVerificationKeyRelationship::AssertionMethod.into()), + KeyReferenceValue, + )); + } + if let Some(key_id) = identity.delegation_key { + set.insert(ProofLeaf::KeyReference( + KeyReferenceKey(key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()), + KeyReferenceValue, + )); + } + if identity.key_agreement_keys.contains(key_id) { + set.insert(ProofLeaf::KeyReference( + KeyReferenceKey(*key_id, KeyRelationship::Encryption), + KeyReferenceValue, + )); + }; + let key_leaf = ProofLeaf::KeyDetails(KeyDetailsKey(*key_id), KeyDetailsValue(key_details.clone())); + if !set.contains(&key_leaf) { + set.insert(key_leaf); + } + Ok(set) + })?; + let encoded_keys: Vec> = leaves.iter().map(|l| l.encoded_key()).collect(); let proof = generate_trie_proof::, _, _, _>(&db, root, &encoded_keys).map_err(|_| ())?; Ok(( @@ -244,13 +279,14 @@ pub mod receiver { use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; use sp_trie::LayoutV1; - #[derive(RuntimeDebug)] + // TODO: Avoid repetition of the same key if it appears multiple times. + #[derive(RuntimeDebug, PartialEq, Eq)] pub struct ProofEntry { pub key: DidPublicKeyDetails, pub relationship: KeyRelationship, } - #[derive(RuntimeDebug)] + #[derive(RuntimeDebug, PartialEq, Eq)] pub struct VerificationResult(pub Vec>); impl From>> for VerificationResult @@ -273,26 +309,25 @@ pub mod receiver { type BlindedValue = Vec>; // TODO: Proper error handling type Error = (); - type LeafKey = LeafKey; - type LeafValue = LeafValue; type ProofDigest = ::Out; + type ProofLeaf = ProofLeaf; type VerificationResult = VerificationResult; fn verify_proof_against_digest( - proof: VersionedIdentityProof, + proof: VersionedIdentityProof, digest: Self::ProofDigest, ) -> Result { use dip_support::v1; use sp_trie::verify_trie_proof; - let proof: v1::Proof<_, _, _> = 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>)>. + let proof: v1::Proof<_, _> = 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>)>. let proof_leaves = proof .revealed .iter() - .map(|(key, value)| (key.encode(), Some(value.encode()))) + .map(|leaf| (leaf.encoded_key(), Some(leaf.encoded_value()))) .collect::, Option>)>>(); verify_trie_proof::, _, _, _>(&digest, &proof.blinded, &proof_leaves).map_err(|_| ())?; @@ -305,8 +340,8 @@ pub mod receiver { .revealed .clone() .into_iter() - .filter_map(|(key, value)| { - if let (LeafKey::KeyDetails(key_id), LeafValue::KeyDetails(key_details)) = (key, value) { + .filter_map(|leaf| { + if let ProofLeaf::KeyDetails(KeyDetailsKey(key_id), KeyDetailsValue(key_details)) = leaf { Some((key_id, key_details)) } else { None @@ -317,15 +352,16 @@ pub mod receiver { let keys: Vec> = proof .revealed .into_iter() - .filter_map(|(key, value)| { - if let (LeafKey::KeyReference(key_id, key_rel), LeafValue::KeyReference) = (key, value) { + .filter_map(|leaf| { + if let ProofLeaf::KeyReference(KeyReferenceKey(key_id, key_relationship), KeyReferenceValue) = 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_rel, + relationship: key_relationship, }) } else { None @@ -343,16 +379,20 @@ mod test { use super::*; - use did::{did_details::DidCreationDetails, KeyIdOf}; + use did::{ + did_details::{DidCreationDetails, DidEncryptionKey}, + KeyIdOf, + }; use frame_support::{ - assert_ok, construct_runtime, parameter_types, traits::Everything, weights::constants::RocksDbWeight, + assert_err, assert_ok, construct_runtime, parameter_types, traits::Everything, + weights::constants::RocksDbWeight, }; use frame_system::{ mocking::{MockBlock, MockUncheckedExtrinsic}, EnsureSigned, RawOrigin, }; use pallet_dip_receiver::traits::IdentityProofVerifier; - use sp_core::{ed25519, ConstU16, ConstU32, ConstU64, Hasher, Pair}; + use sp_core::{ecdsa, ed25519, sr25519, ConstU16, ConstU32, ConstU64, Hasher, Pair}; use sp_io::TestExternalities; use sp_runtime::{ testing::Header, @@ -451,7 +491,7 @@ mod test { type Fee = Fee; type FeeCollector = (); type MaxBlocksTxValidity = MaxBlocksTxValidity; - type MaxNewKeyAgreementKeys = ConstU32<1>; + type MaxNewKeyAgreementKeys = ConstU32<2>; type MaxNumberOfServicesPerDid = ConstU32<1>; type MaxNumberOfTypesPerService = ConstU32<1>; type MaxNumberOfUrlsPerService = ConstU32<1>; @@ -478,7 +518,7 @@ mod test { const ALICE: AccountId = AccountId::new([1u8; 32]); #[test] - fn authentication_merkle_proof_works() { + 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)); @@ -493,18 +533,113 @@ mod test { 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 (root, proof) = DidMerkleRootGenerator::::generate_proof( + &did_details, + vec![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, + vec![<::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"); + println!("{:?}", did_details.key_agreement_keys); + println!("{:?}", did_details.public_keys); + + // 1. Create the DID merkle proof revealing only the authentication key let (root, proof) = DidMerkleRootGenerator::::generate_proof( &did_details, - BTreeSet::from_iter(vec![did_details.authentication_key]), + vec![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.into(), + root + ) + ); + + // 2. Create the DID merkle proof revealing all the keys + let (root, proof) = DidMerkleRootGenerator::::generate_proof( + &did_details, + vec![ + 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."); - println!("{:?} - {:?}", root, proof); + println!("{:?} - {:?} - {:?} bytes", root, proof, proof.encoded_size()); + // Verify the generated merkle proof assert_ok!( DidMerkleProofVerifier::, BlockNumber, Hashing>::verify_proof_against_digest( proof.into(), From b74e2cb1afcc28faf74ab2e863e1425db50d30de Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Fri, 24 Mar 2023 09:10:33 +0100 Subject: [PATCH 07/23] Fix buggy logic for proof generation --- runtimes/common/src/dip.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/runtimes/common/src/dip.rs b/runtimes/common/src/dip.rs index b97868ffe5..86916cd984 100644 --- a/runtimes/common/src/dip.rs +++ b/runtimes/common/src/dip.rs @@ -186,8 +186,6 @@ pub mod sender { #[allow(clippy::type_complexity)] let leaves: BTreeSet, T::BlockNumber>> = key_ids.try_fold(BTreeSet::new(), |mut set, key_id| -> Result<_, ()> { - println!("Key ID: {:?}", key_id); - println!("Public keys: {:?}", identity.public_keys); let key_details = identity.public_keys.get(key_id).ok_or(())?; if *key_id == identity.authentication_key { set.insert(ProofLeaf::KeyReference( @@ -195,15 +193,15 @@ pub mod sender { KeyReferenceValue, )); } - if let Some(key_id) = identity.attestation_key { + if Some(*key_id) == identity.attestation_key { set.insert(ProofLeaf::KeyReference( - KeyReferenceKey(key_id, DidVerificationKeyRelationship::AssertionMethod.into()), + KeyReferenceKey(*key_id, DidVerificationKeyRelationship::AssertionMethod.into()), KeyReferenceValue, )); } - if let Some(key_id) = identity.delegation_key { + if Some(*key_id) == identity.delegation_key { set.insert(ProofLeaf::KeyReference( - KeyReferenceKey(key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()), + KeyReferenceKey(*key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()), KeyReferenceValue, )); } @@ -608,8 +606,6 @@ mod test { did_auth_key.sign(&create_details.encode()).into() )); let did_details = Did::get_did(&did).expect("DID should be present"); - println!("{:?}", did_details.key_agreement_keys); - println!("{:?}", did_details.public_keys); // 1. Create the DID merkle proof revealing only the authentication key let (root, proof) = DidMerkleRootGenerator::::generate_proof( @@ -617,7 +613,6 @@ mod test { vec![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( @@ -638,7 +633,6 @@ mod test { .chain(did_details.key_agreement_keys.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( From dcde80fb1707a092c8556994cddf438a20b74e96 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Fri, 24 Mar 2023 09:36:26 +0100 Subject: [PATCH 08/23] Better tests --- runtimes/common/src/dip.rs | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/runtimes/common/src/dip.rs b/runtimes/common/src/dip.rs index 86916cd984..643d0ccc83 100644 --- a/runtimes/common/src/dip.rs +++ b/runtimes/common/src/dip.rs @@ -381,6 +381,7 @@ mod test { did_details::{DidCreationDetails, DidEncryptionKey}, KeyIdOf, }; + use dip_support::latest::Proof; use frame_support::{ assert_err, assert_ok, construct_runtime, parameter_types, traits::Everything, weights::constants::RocksDbWeight, @@ -542,7 +543,7 @@ mod test { // 1. Create the DID merkle proof revealing only the authentication key let (root, proof) = DidMerkleRootGenerator::::generate_proof( &did_details, - vec![did_details.authentication_key].iter(), + [did_details.authentication_key].iter(), ) .expect("Merkle proof generation should not fail."); println!("{:?} - {:?} - {:?} bytes", root, proof, proof.encoded_size()); @@ -558,7 +559,7 @@ mod test { assert_err!( DidMerkleRootGenerator::::generate_proof( &did_details, - vec![<::Out>::default()].iter() + [<::Out>::default()].iter() ), () ); @@ -610,7 +611,7 @@ mod test { // 1. Create the DID merkle proof revealing only the authentication key let (root, proof) = DidMerkleRootGenerator::::generate_proof( &did_details, - vec![did_details.authentication_key].iter(), + [did_details.authentication_key].iter(), ) .expect("Merkle proof generation should not fail."); // Verify the generated merkle proof @@ -624,7 +625,7 @@ mod test { // 2. Create the DID merkle proof revealing all the keys let (root, proof) = DidMerkleRootGenerator::::generate_proof( &did_details, - vec![ + [ did_details.authentication_key, did_details.attestation_key.unwrap(), did_details.delegation_key.unwrap(), @@ -640,6 +641,31 @@ mod test { root ) ); + + // 2. Create the DID merkle proof revealing only the key reference and not the + // key ID + let (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( + Proof { + blinded: proof.blinded, + revealed: reference_only_authentication_leaf + } + .into(), + root + ), + () + ); }) } } From 7062b599dcc77bec5df213b4e2ed05c156375882 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Fri, 24 Mar 2023 10:23:08 +0100 Subject: [PATCH 09/23] Runtime API implementation compiling! --- Cargo.lock | 9 ++++++ Cargo.toml | 1 + dip-template/runtimes/dip-sender/Cargo.toml | 2 ++ dip-template/runtimes/dip-sender/src/lib.rs | 16 ++++++++-- runtime-api/dip-sender/Cargo.toml | 27 +++++++++++++++++ runtime-api/dip-sender/src/lib.rs | 33 +++++++++++++++++++++ runtimes/common/src/dip.rs | 26 +++++++++------- 7 files changed, 102 insertions(+), 12 deletions(-) create mode 100644 runtime-api/dip-sender/Cargo.toml create mode 100644 runtime-api/dip-sender/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 4f9e4a5963..efc7345114 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2355,6 +2355,7 @@ dependencies = [ "frame-support", "frame-system", "frame-system-rpc-runtime-api", + "kilt-runtime-api-dip-sender", "pallet-aura", "pallet-authorship", "pallet-balances", @@ -4192,6 +4193,14 @@ dependencies = [ "sp-std", ] +[[package]] +name = "kilt-runtime-api-dip-sender" +version = "1.11.0-dev" +dependencies = [ + "parity-scale-codec", + "sp-api", +] + [[package]] name = "kilt-runtime-api-public-credentials" version = "1.11.0-dev" diff --git a/Cargo.toml b/Cargo.toml index 3cb83ae5fa..7ca900e748 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,6 +71,7 @@ dip-sender-runtime-template = {path = "dip-template/runtimes/dip-sender", defaul # Internal runtime API (with default disabled) kilt-runtime-api-did = {path = "runtime-api/did", default-features = false} +kilt-runtime-api-dip-sender = {path = "runtime-api/dip-sender", default-features = false} kilt-runtime-api-public-credentials = {path = "runtime-api/public-credentials", default-features = false} kilt-runtime-api-staking = {path = "runtime-api/staking", default-features = false} diff --git a/dip-template/runtimes/dip-sender/Cargo.toml b/dip-template/runtimes/dip-sender/Cargo.toml index fe9e488624..f2a8ad9622 100644 --- a/dip-template/runtimes/dip-sender/Cargo.toml +++ b/dip-template/runtimes/dip-sender/Cargo.toml @@ -20,6 +20,7 @@ scale-info = {workspace = true, features = ["derive"]} # DIP did.workspace = true dip-support.workspace = true +kilt-runtime-api-dip-sender.workspace = true pallet-dip-sender.workspace = true runtime-common.workspace = true @@ -75,6 +76,7 @@ std = [ "scale-info/std", "did/std", "dip-support/std", + "kilt-runtime-api-dip-sender/std", "pallet-dip-sender/std", "runtime-common/std", "frame-executive/std", diff --git a/dip-template/runtimes/dip-sender/src/lib.rs b/dip-template/runtimes/dip-sender/src/lib.rs index d81684c1e7..c09ffdb033 100644 --- a/dip-template/runtimes/dip-sender/src/lib.rs +++ b/dip-template/runtimes/dip-sender/src/lib.rs @@ -30,7 +30,7 @@ use cumulus_pallet_parachain_system::{ }; use cumulus_primitives_core::CollationInfo; use cumulus_primitives_timestamp::InherentDataProvider; -use did::{DidRawOrigin, EnsureDidOrigin}; +use did::{DidRawOrigin, EnsureDidOrigin, KeyIdOf}; use frame_support::{ construct_runtime, dispatch::DispatchClass, @@ -48,8 +48,10 @@ use frame_system::{ }; use pallet_balances::AccountData; use pallet_collator_selection::IdentityCollator; +use pallet_dip_sender::traits::IdentityProvider; use pallet_session::{FindAccountFromAuthorIndex, PeriodicSessions}; use pallet_transaction_payment::{CurrencyAdapter, FeeDetails, RuntimeDispatchInfo}; +use runtime_common::dip::sender::{CompleteMerkleProof, DidMerkleProof, DidMerkleRootGenerator}; use sp_api::impl_runtime_apis; use sp_consensus_aura::SlotDuration; use sp_core::{crypto::KeyTypeId, ConstU128, ConstU16, OpaqueMetadata}; @@ -60,7 +62,7 @@ use sp_runtime::{ transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, MultiSignature, OpaqueExtrinsic, }; -use sp_std::{prelude::*, time::Duration}; +use sp_std::{collections::btree_set::BTreeSet, prelude::*, time::Duration}; use sp_version::RuntimeVersion; #[cfg(any(feature = "std", test))] @@ -525,4 +527,14 @@ impl_runtime_apis! { ParachainSystem::collect_collation_info(header) } } + + impl kilt_runtime_api_dip_sender::DipSender, BTreeSet>, CompleteMerkleProof>, ()> for Runtime { + fn generate_proof(identifier: DidIdentifier, keys: BTreeSet>) -> Result>, ()> { + if let Ok(Some((did_details, _))) = ::IdentityProvider::retrieve(&identifier) { + DidMerkleRootGenerator::::generate_proof(&did_details, keys.iter()) + } else { + Err(()) + } + } + } } diff --git a/runtime-api/dip-sender/Cargo.toml b/runtime-api/dip-sender/Cargo.toml new file mode 100644 index 0000000000..9de0677e86 --- /dev/null +++ b/runtime-api/dip-sender/Cargo.toml @@ -0,0 +1,27 @@ +[package] +authors.workspace = true +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true +name = "kilt-runtime-api-dip-sender" +description = "Runtime APIs for integrating the DIP sender component." + +[dependencies] +# External dependencies +codec = {package = "parity-scale-codec", workspace = true} + +# Internal dependencies + +# Substrate dependencies +sp-api.workspace = true + +[features] +default = ["std"] +std = [ + "codec/std", + "sp-api/std" +] diff --git a/runtime-api/dip-sender/src/lib.rs b/runtime-api/dip-sender/src/lib.rs new file mode 100644 index 0000000000..cfd2e7180e --- /dev/null +++ b/runtime-api/dip-sender/src/lib.rs @@ -0,0 +1,33 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::Codec; + +sp_api::decl_runtime_apis! { + pub trait DipSender where + DidIdentifier: Codec, + KeyId: Codec, + KeyIds: Codec + IntoIterator, + Success: Codec, + Error: Codec, + { + fn generate_proof(identifier: DidIdentifier, keys: KeyIds) -> Result; + } +} diff --git a/runtimes/common/src/dip.rs b/runtimes/common/src/dip.rs index 643d0ccc83..1a871141d7 100644 --- a/runtimes/common/src/dip.rs +++ b/runtimes/common/src/dip.rs @@ -46,17 +46,17 @@ impl From for KeyRelationship { // KeyDetails(DidPublicKeyDetails), // } -#[derive(Clone, Encode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] pub struct KeyReferenceKey(pub KeyId, pub KeyRelationship); -#[derive(Clone, Encode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] pub struct KeyReferenceValue; -#[derive(Clone, Encode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] pub struct KeyDetailsKey(pub KeyId); -#[derive(Clone, Encode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] pub struct KeyDetailsValue(pub DidPublicKeyDetails); -#[derive(Clone, Encode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] pub enum ProofLeaf { KeyReference(KeyReferenceKey, KeyReferenceValue), KeyDetails(KeyDetailsKey, KeyDetailsValue), @@ -96,6 +96,12 @@ pub mod sender { pub type DidMerkleProof = Proof, ProofLeaf, ::BlockNumber>>; + #[derive(Encode, Decode, TypeInfo)] + pub struct CompleteMerkleProof { + merkle_root: Root, + merkle_proof: Proof, + } + pub struct DidMerkleRootGenerator(PhantomData); impl DidMerkleRootGenerator @@ -173,7 +179,7 @@ pub mod sender { pub fn generate_proof<'a, K>( identity: &DidDetails, mut key_ids: K, - ) -> Result<(T::Hash, DidMerkleProof), ()> + ) -> Result>, ()> where K: Iterator>, { @@ -220,13 +226,13 @@ pub mod sender { let encoded_keys: Vec> = leaves.iter().map(|l| l.encoded_key()).collect(); let proof = generate_trie_proof::, _, _, _>(&db, root, &encoded_keys).map_err(|_| ())?; - Ok(( - root, - DidMerkleProof:: { + Ok(CompleteMerkleProof { + merkle_root: root, + merkle_proof: DidMerkleProof:: { blinded: proof, revealed: leaves.into_iter().collect::>(), }, - )) + }) } } From 523cb827fbb593a3c62570f823abb49ae9bd4213 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Fri, 24 Mar 2023 10:42:25 +0100 Subject: [PATCH 10/23] clippy --all-targets --- dip-template/runtimes/dip-receiver/src/dip.rs | 5 ++- dip-template/runtimes/xcm-tests/src/tests.rs | 2 +- pallets/pallet-dip-receiver/src/lib.rs | 2 +- runtimes/common/src/dip.rs | 33 ++++++++++--------- 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/dip-template/runtimes/dip-receiver/src/dip.rs b/dip-template/runtimes/dip-receiver/src/dip.rs index 60c75d1d2e..18a5ff1b05 100644 --- a/dip-template/runtimes/dip-receiver/src/dip.rs +++ b/dip-template/runtimes/dip-receiver/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 runtime_common::dip::{receiver::DidMerkleProofVerifier, LeafKey, LeafValue}; +use runtime_common::dip::{receiver::DidMerkleProofVerifier, ProofLeaf}; use sp_std::vec::Vec; use crate::{BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; @@ -24,9 +24,8 @@ use crate::{BlockNumber, DidIdentifier, Hash, Hasher, Runtime, RuntimeCall, Runt impl pallet_dip_receiver::Config for Runtime { type BlindedValue = Vec>; type Identifier = DidIdentifier; + type ProofLeaf = ProofLeaf; type ProofDigest = Hash; - type ProofLeafKey = LeafKey; - type ProofLeafValue = LeafValue; type ProofVerifier = DidMerkleProofVerifier; type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index 037561708c..e7befa28ac 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -70,6 +70,6 @@ fn commit_identity() { ))); // 2.2 Verify the proof digest is the same that was sent. let details = DipReceiver::identity_proofs(dip_sender_runtime_template::AccountId::from([0u8; 32])); - assert_eq!(details, Some([0u8; 32])); + assert_eq!(details, Some([0u8; 32].into())); }); } diff --git a/pallets/pallet-dip-receiver/src/lib.rs b/pallets/pallet-dip-receiver/src/lib.rs index da20c3c58b..b8f3ba71f3 100644 --- a/pallets/pallet-dip-receiver/src/lib.rs +++ b/pallets/pallet-dip-receiver/src/lib.rs @@ -51,8 +51,8 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { - type Identifier: Parameter + MaxEncodedLen; type BlindedValue: Parameter; + type Identifier: Parameter + MaxEncodedLen; type ProofLeaf: Parameter; type ProofDigest: Parameter + MaxEncodedLen; type ProofVerifier: IdentityProofVerifier< diff --git a/runtimes/common/src/dip.rs b/runtimes/common/src/dip.rs index 1a871141d7..00c72d9081 100644 --- a/runtimes/common/src/dip.rs +++ b/runtimes/common/src/dip.rs @@ -46,17 +46,17 @@ impl From for KeyRelationship { // KeyDetails(DidPublicKeyDetails), // } -#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] +#[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)] +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] pub struct KeyReferenceValue; -#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] pub struct KeyDetailsKey(pub KeyId); -#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] pub struct KeyDetailsValue(pub DidPublicKeyDetails); -#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] +#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] pub enum ProofLeaf { KeyReference(KeyReferenceKey, KeyReferenceValue), KeyDetails(KeyDetailsKey, KeyDetailsValue), @@ -96,10 +96,10 @@ pub mod sender { pub type DidMerkleProof = Proof, ProofLeaf, ::BlockNumber>>; - #[derive(Encode, Decode, TypeInfo)] + #[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] pub struct CompleteMerkleProof { - merkle_root: Root, - merkle_proof: Proof, + pub root: Root, + pub proof: Proof, } pub struct DidMerkleRootGenerator(PhantomData); @@ -227,8 +227,8 @@ pub mod sender { let proof = generate_trie_proof::, _, _, _>(&db, root, &encoded_keys).map_err(|_| ())?; Ok(CompleteMerkleProof { - merkle_root: root, - merkle_proof: DidMerkleProof:: { + root, + proof: DidMerkleProof:: { blinded: proof, revealed: leaves.into_iter().collect::>(), }, @@ -379,7 +379,10 @@ pub mod receiver { #[cfg(test)] mod test { - use crate::dip::{receiver::DidMerkleProofVerifier, sender::DidMerkleRootGenerator}; + use crate::dip::{ + receiver::DidMerkleProofVerifier, + sender::{CompleteMerkleProof, DidMerkleRootGenerator}, + }; use super::*; @@ -547,7 +550,7 @@ mod test { let did_details = Did::get_did(&did).expect("DID should be present"); // 1. Create the DID merkle proof revealing only the authentication key - let (root, proof) = DidMerkleRootGenerator::::generate_proof( + let CompleteMerkleProof { root, proof } = DidMerkleRootGenerator::::generate_proof( &did_details, [did_details.authentication_key].iter(), ) @@ -615,7 +618,7 @@ mod test { let did_details = Did::get_did(&did).expect("DID should be present"); // 1. Create the DID merkle proof revealing only the authentication key - let (root, proof) = DidMerkleRootGenerator::::generate_proof( + let CompleteMerkleProof { root, proof } = DidMerkleRootGenerator::::generate_proof( &did_details, [did_details.authentication_key].iter(), ) @@ -629,7 +632,7 @@ mod test { ); // 2. Create the DID merkle proof revealing all the keys - let (root, proof) = DidMerkleRootGenerator::::generate_proof( + let CompleteMerkleProof { root, proof } = DidMerkleRootGenerator::::generate_proof( &did_details, [ did_details.authentication_key, @@ -650,7 +653,7 @@ mod test { // 2. Create the DID merkle proof revealing only the key reference and not the // key ID - let (root, proof) = DidMerkleRootGenerator::::generate_proof( + let CompleteMerkleProof { root, proof } = DidMerkleRootGenerator::::generate_proof( &did_details, [did_details.authentication_key].iter(), ) From 1bde773437e6df1a00b8f2a2d963be9d681d73f5 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Fri, 24 Mar 2023 11:23:52 +0100 Subject: [PATCH 11/23] Wip on the XCM tests --- Cargo.lock | 6 +++- dip-template/runtimes/dip-receiver/src/lib.rs | 6 ++-- dip-template/runtimes/dip-sender/src/lib.rs | 6 ++-- dip-template/runtimes/xcm-tests/Cargo.toml | 10 +++--- dip-template/runtimes/xcm-tests/src/para.rs | 33 ++++++++++++++++++- 5 files changed, 49 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index efc7345114..97440c01ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2399,14 +2399,16 @@ dependencies = [ ] [[package]] -name = "dip-templates-xmc-tests" +name = "dip-templates-xcm-tests" version = "1.11.0-dev" dependencies = [ "cumulus-pallet-xcmp-queue", + "did", "dip-receiver-runtime-template", "dip-sender-runtime-template", "frame-support", "frame-system", + "kilt-support", "pallet-balances", "parachain-info", "parity-scale-codec", @@ -2415,8 +2417,10 @@ dependencies = [ "polkadot-runtime-parachains", "rococo-runtime", "scale-info", + "sp-core", "sp-io", "sp-runtime", + "sp-std", "xcm", "xcm-emulator", "xcm-executor", diff --git a/dip-template/runtimes/dip-receiver/src/lib.rs b/dip-template/runtimes/dip-receiver/src/lib.rs index 6f9db47ab0..dc217abbe2 100644 --- a/dip-template/runtimes/dip-receiver/src/lib.rs +++ b/dip-template/runtimes/dip-receiver/src/lib.rs @@ -56,9 +56,9 @@ use sp_core::{crypto::KeyTypeId, ConstU128, ConstU16, OpaqueMetadata}; use sp_inherents::{CheckInherentsResult, InherentData}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, OpaqueKeys, Verify}, + traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, OpaqueKeys}, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, MultiSignature, OpaqueExtrinsic, + AccountId32, ApplyExtrinsicResult, MultiSignature, OpaqueExtrinsic, }; use sp_std::{prelude::*, time::Duration}; use sp_version::RuntimeVersion; @@ -73,7 +73,7 @@ pub use sp_runtime::BuildStorage; #[cfg(feature = "std")] use sp_version::NativeVersion; -pub type AccountId = <::Signer as IdentifyAccount>::AccountId; +pub type AccountId = AccountId32; pub type Address = MultiAddress; pub type Balance = u128; pub type Block = generic::Block; diff --git a/dip-template/runtimes/dip-sender/src/lib.rs b/dip-template/runtimes/dip-sender/src/lib.rs index c09ffdb033..02f8ef8ab6 100644 --- a/dip-template/runtimes/dip-sender/src/lib.rs +++ b/dip-template/runtimes/dip-sender/src/lib.rs @@ -58,9 +58,9 @@ use sp_core::{crypto::KeyTypeId, ConstU128, ConstU16, OpaqueMetadata}; use sp_inherents::{CheckInherentsResult, InherentData}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, OpaqueKeys, Verify}, + traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, OpaqueKeys}, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, MultiSignature, OpaqueExtrinsic, + AccountId32, ApplyExtrinsicResult, MultiSignature, OpaqueExtrinsic, }; use sp_std::{collections::btree_set::BTreeSet, prelude::*, time::Duration}; use sp_version::RuntimeVersion; @@ -75,7 +75,7 @@ mod dip; mod xcm_config; pub use crate::{dip::*, xcm_config::*}; -pub type AccountId = <::Signer as IdentifyAccount>::AccountId; +pub type AccountId = AccountId32; pub type Address = MultiAddress; pub type Balance = u128; pub type Block = generic::Block; diff --git a/dip-template/runtimes/xcm-tests/Cargo.toml b/dip-template/runtimes/xcm-tests/Cargo.toml index 37619c2724..9172f70046 100644 --- a/dip-template/runtimes/xcm-tests/Cargo.toml +++ b/dip-template/runtimes/xcm-tests/Cargo.toml @@ -7,18 +7,18 @@ license-file.workspace = true readme.workspace = true repository.workspace = true version.workspace = true -name = "dip-templates-xmc-tests" +name = "dip-templates-xcm-tests" description = "XCM integration tests for the KILT Decentralized Identity Provider (DIP) sender and receiver templates." -[dev-dependencies] -cumulus-pallet-xcmp-queue = { workspace = true, features = ["std"] } - [dependencies] codec = {package = "parity-scale-codec", workspace = true, features = ["std", "derive"]} +cumulus-pallet-xcmp-queue = { workspace = true, features = ["std"] } +did = { workspace = true, features = ["std"] } dip-receiver-runtime-template = { workspace = true, features = ["std"] } dip-sender-runtime-template = { workspace = true, features = ["std"] } frame-support = { workspace = true, features = ["std"] } frame-system = { workspace = true, features = ["std"] } +kilt-support = { workspace = true, features = ["std"] } pallet-balances = { workspace = true, features = ["std"] } parachain-info = { workspace = true, features = ["std"] } polkadot-parachain = { workspace = true, features = ["std"] } @@ -26,8 +26,10 @@ polkadot-primitives = { workspace = true, features = ["std"] } polkadot-runtime-parachains = { workspace = true, features = ["std"] } rococo-runtime = { workspace = true, features = ["std"] } scale-info = { workspace = true, features = ["std"] } +sp-core = { workspace = true, features = ["std"] } sp-io = { workspace = true, features = ["std"] } sp-runtime = { workspace = true, features = ["std"] } +sp-std = { workspace = true, features = ["std"] } xcm = { workspace = true, features = ["std"] } xcm-emulator = { git = "https://github.com/shaunxw/xcm-simulator", branch = "master" } xcm-executor = { workspace = true, features = ["std"] } diff --git a/dip-template/runtimes/xcm-tests/src/para.rs b/dip-template/runtimes/xcm-tests/src/para.rs index ca30e62ba9..4b71176941 100644 --- a/dip-template/runtimes/xcm-tests/src/para.rs +++ b/dip-template/runtimes/xcm-tests/src/para.rs @@ -22,12 +22,41 @@ use sp_io::TestExternalities; use xcm_emulator::decl_test_parachain; pub(super) mod sender { - pub(crate) use dip_sender_runtime_template::{DmpQueue, Runtime, RuntimeOrigin, XcmpQueue}; + use did::did_details::{DidCreationDetails, DidDetails, DidVerificationKey}; + pub(crate) use dip_sender_runtime_template::{DidIdentifier, DmpQueue, Runtime, RuntimeOrigin, XcmpQueue}; use super::*; pub const PARA_ID: u32 = 2_000; + fn generate_did_details(auth_key: DidVerificationKey) -> DidDetails { + use did::did_details::{DidEncryptionKey, DidVerificationKey}; + use dip_sender_runtime_template::AccountId; + use kilt_support::Deposit; + use sp_core::{ecdsa, ed25519, sr25519, DeriveJunction, Pair}; + use sp_std::collections::btree_set::BTreeSet; + + let att_key: DidVerificationKey = sr25519::Pair::from_seed(&[100u8; 32]).public().into(); + let del_key: DidVerificationKey = ecdsa::Pair::from_seed(&[101u8; 32]).public().into(); + + let mut base_details = DidDetails::new(auth_key, 0u64.into(), ) + DidDetails::from_creation_details( + DidCreationDetails { + did, + // Not relevant + submitter: AccountId::new([0u8; 32]), + new_key_agreement_keys: BTreeSet::from_iter([DidEncryptionKey::X25519([3u8; 32])].into_iter()) + .try_into() + .unwrap(), + new_attestation_key: Some(att_key.public().into()), + new_delegation_key: Some(del_key.public().into()), + new_service_details: vec![], + }, + auth_key.public().into(), + ) + .unwrap() + } + pub(crate) fn para_ext() -> TestExternalities { use dip_sender_runtime_template::System; @@ -43,7 +72,9 @@ pub(super) mod sender { .unwrap(); let mut ext = TestExternalities::new(t); + let (did, details) = generate_did_details(); ext.execute_with(|| { + did::pallet::Did::::insert(&did, details); System::set_block_number(1); }); ext From 2033e2216e21c8f4892a22cf005a6ed575680620 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Fri, 24 Mar 2023 12:15:18 +0100 Subject: [PATCH 12/23] WIP integration test on receiver chain --- Cargo.lock | 3 ++ dip-template/runtimes/xcm-tests/Cargo.toml | 3 ++ dip-template/runtimes/xcm-tests/src/para.rs | 45 ++++++++++-------- dip-template/runtimes/xcm-tests/src/tests.rs | 49 ++++++++++++++++---- 4 files changed, 70 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 97440c01ab..5b03ccc153 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2406,16 +2406,19 @@ dependencies = [ "did", "dip-receiver-runtime-template", "dip-sender-runtime-template", + "dip-support", "frame-support", "frame-system", "kilt-support", "pallet-balances", + "pallet-did-lookup", "parachain-info", "parity-scale-codec", "polkadot-parachain", "polkadot-primitives", "polkadot-runtime-parachains", "rococo-runtime", + "runtime-common", "scale-info", "sp-core", "sp-io", diff --git a/dip-template/runtimes/xcm-tests/Cargo.toml b/dip-template/runtimes/xcm-tests/Cargo.toml index 9172f70046..6e6c10a901 100644 --- a/dip-template/runtimes/xcm-tests/Cargo.toml +++ b/dip-template/runtimes/xcm-tests/Cargo.toml @@ -16,15 +16,18 @@ cumulus-pallet-xcmp-queue = { workspace = true, features = ["std"] } did = { workspace = true, features = ["std"] } dip-receiver-runtime-template = { workspace = true, features = ["std"] } dip-sender-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-support = { workspace = true, features = ["std"] } pallet-balances = { workspace = true, features = ["std"] } +pallet-did-lookup = { workspace = true, features = ["std"] } parachain-info = { workspace = true, features = ["std"] } polkadot-parachain = { workspace = true, features = ["std"] } polkadot-primitives = { workspace = true, features = ["std"] } polkadot-runtime-parachains = { workspace = true, features = ["std"] } rococo-runtime = { workspace = true, features = ["std"] } +runtime-common = { workspace = true, features = ["std"] } scale-info = { workspace = true, features = ["std"] } sp-core = { workspace = true, features = ["std"] } sp-io = { workspace = true, features = ["std"] } diff --git a/dip-template/runtimes/xcm-tests/src/para.rs b/dip-template/runtimes/xcm-tests/src/para.rs index 4b71176941..e3da79e9e8 100644 --- a/dip-template/runtimes/xcm-tests/src/para.rs +++ b/dip-template/runtimes/xcm-tests/src/para.rs @@ -22,39 +22,43 @@ use sp_io::TestExternalities; use xcm_emulator::decl_test_parachain; pub(super) mod sender { - use did::did_details::{DidCreationDetails, DidDetails, DidVerificationKey}; + use did::did_details::DidDetails; pub(crate) use dip_sender_runtime_template::{DidIdentifier, DmpQueue, Runtime, RuntimeOrigin, XcmpQueue}; + use sp_core::{ed25519, Pair}; use super::*; pub const PARA_ID: u32 = 2_000; - fn generate_did_details(auth_key: DidVerificationKey) -> DidDetails { + pub(crate) fn did_auth_key() -> ed25519::Pair { + ed25519::Pair::from_seed(&[200u8; 32]) + } + + fn generate_did_details() -> DidDetails { use did::did_details::{DidEncryptionKey, DidVerificationKey}; use dip_sender_runtime_template::AccountId; - use kilt_support::Deposit; - use sp_core::{ecdsa, ed25519, sr25519, DeriveJunction, Pair}; - use sp_std::collections::btree_set::BTreeSet; + use kilt_support::deposit::Deposit; + use sp_core::{ecdsa, sr25519}; + let auth_key: DidVerificationKey = did_auth_key().public().into(); let att_key: DidVerificationKey = sr25519::Pair::from_seed(&[100u8; 32]).public().into(); let del_key: DidVerificationKey = ecdsa::Pair::from_seed(&[101u8; 32]).public().into(); - let mut base_details = DidDetails::new(auth_key, 0u64.into(), ) - DidDetails::from_creation_details( - DidCreationDetails { - did, - // Not relevant - submitter: AccountId::new([0u8; 32]), - new_key_agreement_keys: BTreeSet::from_iter([DidEncryptionKey::X25519([3u8; 32])].into_iter()) - .try_into() - .unwrap(), - new_attestation_key: Some(att_key.public().into()), - new_delegation_key: Some(del_key.public().into()), - new_service_details: vec![], + let mut details = DidDetails::new( + auth_key, + 0u32, + Deposit { + amount: 1u64.into(), + owner: AccountId::new([1u8; 32]), }, - auth_key.public().into(), ) - .unwrap() + .unwrap(); + details.update_attestation_key(att_key, 0u32.into()).unwrap(); + details.update_delegation_key(del_key, 0u32.into()).unwrap(); + details + .add_key_agreement_key(DidEncryptionKey::X25519([100u8; 32]), 0u32.into()) + .unwrap(); + details } pub(crate) fn para_ext() -> TestExternalities { @@ -72,7 +76,8 @@ pub(super) mod sender { .unwrap(); let mut ext = TestExternalities::new(t); - let (did, details) = generate_did_details(); + let did: DidIdentifier = did_auth_key().public().into(); + let details = generate_did_details(); ext.execute_with(|| { did::pallet::Did::::insert(&did, details); System::set_block_number(1); diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index e7befa28ac..e9649addca 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -18,13 +18,12 @@ use super::*; -use dip_receiver_runtime_template::{ - AccountId as ReceiverAccountId, DidIdentifier as ReceiverDidIdentifier, DipReceiver, -}; -use dip_sender_runtime_template::DipSender; - +use did::Did; +use dip_support::latest::Proof; use frame_support::{assert_ok, weights::Weight}; use frame_system::RawOrigin; +use runtime_common::dip::sender::{CompleteMerkleProof, DidMerkleRootGenerator}; +use sp_core::Pair; use xcm::latest::{ Junction::Parachain, Junctions::{Here, X1}, @@ -32,10 +31,18 @@ use xcm::latest::{ }; use xcm_emulator::TestExt; +use dip_receiver_runtime_template::{ + AccountId as ReceiverAccountId, DidIdentifier, DipReceiver, Runtime as ReceiverRuntime, + RuntimeCall as ReceiverRuntimeCall, +}; +use dip_sender_runtime_template::{AccountId as SenderAccountId, DipSender, Runtime as SenderRuntime}; + #[test] fn commit_identity() { Network::reset(); + let did: DidIdentifier = para::sender::did_auth_key().public().into(); + ReceiverParachain::execute_with(|| { use dip_receiver_runtime_template::Balances; use para::receiver::sender_parachain_account; @@ -47,8 +54,8 @@ fn commit_identity() { // 1. Send identity proof from DIP sender to DIP receiver. SenderParachain::execute_with(|| { assert_ok!(DipSender::commit_identity( - RawOrigin::Signed(ReceiverAccountId::from([0u8; 32])).into(), - ReceiverDidIdentifier::from([0u8; 32]), + RawOrigin::Signed(SenderAccountId::from([0u8; 32])).into(), + did.clone(), Box::new(ParentThen(X1(Parachain(para::receiver::PARA_ID))).into()), Box::new((Here, 1_000_000_000).into()), Weight::from_ref_time(4_000), @@ -68,8 +75,30 @@ fn commit_identity() { weight: _ }) ))); - // 2.2 Verify the proof digest is the same that was sent. - let details = DipReceiver::identity_proofs(dip_sender_runtime_template::AccountId::from([0u8; 32])); - assert_eq!(details, Some([0u8; 32].into())); + // 2.2 Verify the proof digest was stored correctly. + assert!(DipReceiver::identity_proofs(&did).is_some()); + }); + // 3. Call an extrinsic on the receiver chain with a valid proof + let did_details = + SenderParachain::execute_with(|| Did::get(&did).expect("DID details should be stored on the sender chain.")); + // 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 receiver chain with the generated + // proof + ReceiverParachain::execute_with(|| { + assert_ok!(DipReceiver::dispatch_as( + RawOrigin::Signed(ReceiverAccountId::new([100u8; 32])).into(), + did.clone(), + Proof { + blinded: proof.blinded, + revealed: proof.revealed, + } + .into(), + Box::new(ReceiverRuntimeCall::DidLookup(pallet_did_lookup::Call::< + ReceiverRuntime, + >::associate_sender {})), + )); }); } From 2cd3360fcbf2d564112654a22bb4802991a93795 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Fri, 24 Mar 2023 14:02:01 +0100 Subject: [PATCH 13/23] Integration test completed --- dip-template/runtimes/xcm-tests/src/para.rs | 10 +++++++--- dip-template/runtimes/xcm-tests/src/tests.rs | 18 +++++++----------- pallets/pallet-did-lookup/src/lib.rs | 6 ++++++ 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/dip-template/runtimes/xcm-tests/src/para.rs b/dip-template/runtimes/xcm-tests/src/para.rs index e3da79e9e8..c5910f55d7 100644 --- a/dip-template/runtimes/xcm-tests/src/para.rs +++ b/dip-template/runtimes/xcm-tests/src/para.rs @@ -98,7 +98,7 @@ pub(super) mod sender { pub(super) mod receiver { pub(crate) use dip_receiver_runtime_template::{ - AccountId, AssetTransactorLocationConverter, Balance, DmpQueue, Runtime, RuntimeOrigin, XcmpQueue, + AccountId, AssetTransactorLocationConverter, Balance, DmpQueue, Runtime, RuntimeOrigin, XcmpQueue, UNIT, }; use xcm::latest::{Junction::Parachain, Junctions::X1, ParentThen}; @@ -107,7 +107,8 @@ pub(super) mod receiver { use super::*; pub const PARA_ID: u32 = 2_001; - const INITIAL_BALANCE: Balance = 1_000_000_000; + pub const DISPATCHER_ACCOUNT: AccountId = AccountId::new([90u8; 32]); + const INITIAL_BALANCE: Balance = 100_000 * UNIT; pub(crate) fn sender_parachain_account() -> AccountId { AssetTransactorLocationConverter::convert(ParentThen(X1(Parachain(sender::PARA_ID))).into()) @@ -129,7 +130,10 @@ pub(super) mod receiver { .unwrap(); pallet_balances::GenesisConfig:: { - balances: vec![(sender_parachain_account(), INITIAL_BALANCE)], + balances: vec![ + (sender_parachain_account(), INITIAL_BALANCE), + (DISPATCHER_ACCOUNT, INITIAL_BALANCE), + ], } .assimilate_storage(&mut t) .unwrap(); diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index e9649addca..4c6be6c740 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -22,6 +22,7 @@ use did::Did; use dip_support::latest::Proof; use frame_support::{assert_ok, weights::Weight}; use frame_system::RawOrigin; +use pallet_did_lookup::linkable_account::LinkableAccountId; use runtime_common::dip::sender::{CompleteMerkleProof, DidMerkleRootGenerator}; use sp_core::Pair; use xcm::latest::{ @@ -32,8 +33,7 @@ use xcm::latest::{ use xcm_emulator::TestExt; use dip_receiver_runtime_template::{ - AccountId as ReceiverAccountId, DidIdentifier, DipReceiver, Runtime as ReceiverRuntime, - RuntimeCall as ReceiverRuntimeCall, + DidIdentifier, DidLookup, DipReceiver, Runtime as ReceiverRuntime, RuntimeCall as ReceiverRuntimeCall, }; use dip_sender_runtime_template::{AccountId as SenderAccountId, DipSender, Runtime as SenderRuntime}; @@ -43,14 +43,6 @@ fn commit_identity() { let did: DidIdentifier = para::sender::did_auth_key().public().into(); - ReceiverParachain::execute_with(|| { - use dip_receiver_runtime_template::Balances; - use para::receiver::sender_parachain_account; - - let sender_balance = Balances::free_balance(sender_parachain_account()); - println!("Sender balance: {:?}", sender_balance); - }); - // 1. Send identity proof from DIP sender to DIP receiver. SenderParachain::execute_with(|| { assert_ok!(DipSender::commit_identity( @@ -89,7 +81,7 @@ fn commit_identity() { // proof ReceiverParachain::execute_with(|| { assert_ok!(DipReceiver::dispatch_as( - RawOrigin::Signed(ReceiverAccountId::new([100u8; 32])).into(), + RawOrigin::Signed(para::receiver::DISPATCHER_ACCOUNT).into(), did.clone(), Proof { blinded: proof.blinded, @@ -100,5 +92,9 @@ fn commit_identity() { ReceiverRuntime, >::associate_sender {})), )); + // Verify the account -> DID link exists and contains the right information + let linked_did = DidLookup::connected_dids::(para::receiver::DISPATCHER_ACCOUNT.into()) + .map(|link| link.did); + assert_eq!(linked_did, Some(did)); }); } diff --git a/pallets/pallet-did-lookup/src/lib.rs b/pallets/pallet-did-lookup/src/lib.rs index d7802701dd..1214320385 100644 --- a/pallets/pallet-did-lookup/src/lib.rs +++ b/pallets/pallet-did-lookup/src/lib.rs @@ -275,7 +275,11 @@ pub mod pallet { #[pallet::call_index(1)] #[pallet::weight(::WeightInfo::associate_sender())] pub fn associate_sender(origin: OriginFor) -> DispatchResult { + #[cfg(feature = "std")] + println!("AAAAA"); let source = ::EnsureOrigin::ensure_origin(origin)?; + #[cfg(feature = "std")] + println!("BBBBB"); ensure!( >>::can_reserve( @@ -284,6 +288,8 @@ pub mod pallet { ), Error::::InsufficientFunds ); + #[cfg(feature = "std")] + println!("CCCCC"); Self::add_association(source.sender(), source.subject(), source.sender().into())?; Ok(()) From 94ee61eb37b7355f9af0c3b840d0038e2e5e0caa Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Fri, 24 Mar 2023 16:43:20 +0100 Subject: [PATCH 14/23] Set spec_version of templates to 11000 to avoid type definitions mixups --- dip-template/runtimes/dip-receiver/src/lib.rs | 2 +- dip-template/runtimes/dip-sender/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dip-template/runtimes/dip-receiver/src/lib.rs b/dip-template/runtimes/dip-receiver/src/lib.rs index dc217abbe2..c678bf0c2c 100644 --- a/dip-template/runtimes/dip-receiver/src/lib.rs +++ b/dip-template/runtimes/dip-receiver/src/lib.rs @@ -150,7 +150,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("dip-receiver-runtime-template"), impl_name: create_runtime_str!("dip-receiver-runtime-template"), authoring_version: 1, - spec_version: 1, + spec_version: 11100, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, diff --git a/dip-template/runtimes/dip-sender/src/lib.rs b/dip-template/runtimes/dip-sender/src/lib.rs index 02f8ef8ab6..3837f3b207 100644 --- a/dip-template/runtimes/dip-sender/src/lib.rs +++ b/dip-template/runtimes/dip-sender/src/lib.rs @@ -151,7 +151,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("dip-sender-runtime-template"), impl_name: create_runtime_str!("dip-sender-runtime-template"), authoring_version: 1, - spec_version: 1, + spec_version: 11100, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From cd9bc0dd386dbaa32c318d970dd9177e7b06c378 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Mon, 27 Mar 2023 10:05:05 +0200 Subject: [PATCH 15/23] Replace BTreeSet input with Vec --- dip-template/runtimes/dip-sender/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dip-template/runtimes/dip-sender/src/lib.rs b/dip-template/runtimes/dip-sender/src/lib.rs index 3837f3b207..c97b4bdbb5 100644 --- a/dip-template/runtimes/dip-sender/src/lib.rs +++ b/dip-template/runtimes/dip-sender/src/lib.rs @@ -62,7 +62,7 @@ use sp_runtime::{ transaction_validity::{TransactionSource, TransactionValidity}, AccountId32, ApplyExtrinsicResult, MultiSignature, OpaqueExtrinsic, }; -use sp_std::{collections::btree_set::BTreeSet, prelude::*, time::Duration}; +use sp_std::{prelude::*, time::Duration}; use sp_version::RuntimeVersion; #[cfg(any(feature = "std", test))] @@ -528,8 +528,9 @@ impl_runtime_apis! { } } - impl kilt_runtime_api_dip_sender::DipSender, BTreeSet>, CompleteMerkleProof>, ()> for Runtime { - fn generate_proof(identifier: DidIdentifier, keys: BTreeSet>) -> Result>, ()> { + // 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. + impl kilt_runtime_api_dip_sender::DipSender, 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 { From 627a506892b9309c634d21944c5ef56ca7ad5741 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Mon, 27 Mar 2023 10:53:52 +0200 Subject: [PATCH 16/23] More changes --- crates/dip/src/v1.rs | 1 + dip-template/runtimes/dip-sender/src/lib.rs | 1 + dip-template/runtimes/xcm-tests/src/para.rs | 3 +- pallets/pallet-did-lookup/src/lib.rs | 6 ---- pallets/pallet-dip-receiver/src/lib.rs | 2 +- runtimes/common/src/dip.rs | 34 ++++++++------------- 6 files changed, 18 insertions(+), 29 deletions(-) diff --git a/crates/dip/src/v1.rs b/crates/dip/src/v1.rs index 7098c72918..4e51348c2a 100644 --- a/crates/dip/src/v1.rs +++ b/crates/dip/src/v1.rs @@ -30,5 +30,6 @@ pub enum IdentityProofAction { #[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo, Default)] pub struct Proof { 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-sender/src/lib.rs b/dip-template/runtimes/dip-sender/src/lib.rs index c97b4bdbb5..d13871665f 100644 --- a/dip-template/runtimes/dip-sender/src/lib.rs +++ b/dip-template/runtimes/dip-sender/src/lib.rs @@ -529,6 +529,7 @@ 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_sender::DipSender, Vec>, CompleteMerkleProof>, ()> for Runtime { fn generate_proof(identifier: DidIdentifier, keys: Vec>) -> Result>, ()> { if let Ok(Some((did_details, _))) = ::IdentityProvider::retrieve(&identifier) { diff --git a/dip-template/runtimes/xcm-tests/src/para.rs b/dip-template/runtimes/xcm-tests/src/para.rs index c5910f55d7..34ff528aea 100644 --- a/dip-template/runtimes/xcm-tests/src/para.rs +++ b/dip-template/runtimes/xcm-tests/src/para.rs @@ -22,8 +22,9 @@ use sp_io::TestExternalities; use xcm_emulator::decl_test_parachain; pub(super) mod sender { - use did::did_details::DidDetails; pub(crate) use dip_sender_runtime_template::{DidIdentifier, DmpQueue, Runtime, RuntimeOrigin, XcmpQueue}; + + use did::did_details::DidDetails; use sp_core::{ed25519, Pair}; use super::*; diff --git a/pallets/pallet-did-lookup/src/lib.rs b/pallets/pallet-did-lookup/src/lib.rs index 1214320385..d7802701dd 100644 --- a/pallets/pallet-did-lookup/src/lib.rs +++ b/pallets/pallet-did-lookup/src/lib.rs @@ -275,11 +275,7 @@ pub mod pallet { #[pallet::call_index(1)] #[pallet::weight(::WeightInfo::associate_sender())] pub fn associate_sender(origin: OriginFor) -> DispatchResult { - #[cfg(feature = "std")] - println!("AAAAA"); let source = ::EnsureOrigin::ensure_origin(origin)?; - #[cfg(feature = "std")] - println!("BBBBB"); ensure!( >>::can_reserve( @@ -288,8 +284,6 @@ pub mod pallet { ), Error::::InsufficientFunds ); - #[cfg(feature = "std")] - println!("CCCCC"); Self::add_association(source.sender(), source.subject(), source.sender().into())?; Ok(()) diff --git a/pallets/pallet-dip-receiver/src/lib.rs b/pallets/pallet-dip-receiver/src/lib.rs index b8f3ba71f3..4c33ab8fae 100644 --- a/pallets/pallet-dip-receiver/src/lib.rs +++ b/pallets/pallet-dip-receiver/src/lib.rs @@ -57,8 +57,8 @@ pub mod pallet { type ProofDigest: Parameter + MaxEncodedLen; type ProofVerifier: IdentityProofVerifier< BlindedValue = Self::BlindedValue, - ProofLeaf = Self::ProofLeaf, ProofDigest = Self::ProofDigest, + ProofLeaf = Self::ProofLeaf, >; type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin>; type RuntimeEvent: From> + IsType<::RuntimeEvent>; diff --git a/runtimes/common/src/dip.rs b/runtimes/common/src/dip.rs index 00c72d9081..e3ec5e2740 100644 --- a/runtimes/common/src/dip.rs +++ b/runtimes/common/src/dip.rs @@ -34,18 +34,6 @@ impl From for KeyRelationship { } } -// #[derive(Clone, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, -// PartialOrd, Ord)] pub enum LeafKey { -// KeyReference(KeyId, KeyRelationship), -// KeyDetails(KeyId), -// } - -// #[derive(Clone, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo)] -// pub enum LeafValue { -// KeyReference, -// KeyDetails(DidPublicKeyDetails), -// } - #[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)] @@ -217,9 +205,10 @@ pub mod sender { KeyReferenceValue, )); }; - let key_leaf = ProofLeaf::KeyDetails(KeyDetailsKey(*key_id), KeyDetailsValue(key_details.clone())); - if !set.contains(&key_leaf) { - set.insert(key_leaf); + let key_details_leaf = + ProofLeaf::KeyDetails(KeyDetailsKey(*key_id), KeyDetailsValue(key_details.clone())); + if !set.contains(&key_details_leaf) { + set.insert(key_details_leaf); } Ok(set) })?; @@ -268,7 +257,7 @@ pub mod sender { did::Pallet::::get_deleted_did(identifier), ) { (Some(details), _) => Ok(Some((details, ()))), - (None, Some(_)) => Ok(None), + (_, Some(_)) => Ok(None), _ => Err(()), } } @@ -283,7 +272,8 @@ pub mod receiver { use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; use sp_trie::LayoutV1; - // TODO: Avoid repetition of the same key if it appears multiple times. + // 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)] pub struct ProofEntry { pub key: DidPublicKeyDetails, @@ -310,7 +300,7 @@ pub mod receiver { BlockNumber: MaxEncodedLen + Clone + Ord, Hasher: sp_core::Hasher, { - type BlindedValue = Vec>; + type BlindedValue = Vec; // TODO: Proper error handling type Error = (); type ProofDigest = ::Out; @@ -352,7 +342,9 @@ pub mod receiver { } }) .collect(); - // Create a list of the revealed keys + // 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() @@ -379,13 +371,13 @@ pub mod receiver { #[cfg(test)] mod test { + use super::*; + use crate::dip::{ receiver::DidMerkleProofVerifier, sender::{CompleteMerkleProof, DidMerkleRootGenerator}, }; - use super::*; - use did::{ did_details::{DidCreationDetails, DidEncryptionKey}, KeyIdOf, From fc5b4a15cf8b6b8db0d41d1973c199f03de7160b Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Mon, 27 Mar 2023 10:58:35 +0200 Subject: [PATCH 17/23] Last fixes --- dip-template/runtimes/xcm-tests/src/para.rs | 6 +++--- pallets/pallet-dip-receiver/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dip-template/runtimes/xcm-tests/src/para.rs b/dip-template/runtimes/xcm-tests/src/para.rs index 34ff528aea..2649bf36ae 100644 --- a/dip-template/runtimes/xcm-tests/src/para.rs +++ b/dip-template/runtimes/xcm-tests/src/para.rs @@ -54,10 +54,10 @@ pub(super) mod sender { }, ) .unwrap(); - details.update_attestation_key(att_key, 0u32.into()).unwrap(); - details.update_delegation_key(del_key, 0u32.into()).unwrap(); + details.update_attestation_key(att_key, 0u32).unwrap(); + details.update_delegation_key(del_key, 0u32).unwrap(); details - .add_key_agreement_key(DidEncryptionKey::X25519([100u8; 32]), 0u32.into()) + .add_key_agreement_key(DidEncryptionKey::X25519([100u8; 32]), 0u32) .unwrap(); details } diff --git a/pallets/pallet-dip-receiver/Cargo.toml b/pallets/pallet-dip-receiver/Cargo.toml index a6357271b0..da05db80ec 100644 --- a/pallets/pallet-dip-receiver/Cargo.toml +++ b/pallets/pallet-dip-receiver/Cargo.toml @@ -37,5 +37,5 @@ std = [ ] runtime-benchmarks = [ "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks" + "frame-system/runtime-benchmarks", ] From a99b8364a4b9c602a74deb72af2ae9648add3fc0 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Mon, 27 Mar 2023 11:12:52 +0200 Subject: [PATCH 18/23] Add some comments for easier understanding --- runtimes/common/src/dip.rs | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/runtimes/common/src/dip.rs b/runtimes/common/src/dip.rs index e3ec5e2740..5c331cb263 100644 --- a/runtimes/common/src/dip.rs +++ b/runtimes/common/src/dip.rs @@ -46,7 +46,12 @@ pub struct KeyDetailsValue(pub DidPublicKeyDetails { + // 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), } @@ -96,6 +101,17 @@ pub mod sender { where T: did::Config, { + // Calls the function in the `sp_trie` crate to generate the merkle root given + // the provided `DidDetails`. + // Each key in the merkle tree is added in the following way: + // - keys in the `public_keys` map are added by value in the merkle tree, with + // the leaf key being the key ID and the value being the key details + // - keys everywhere else in the DidDetails are added by reference, with the + // leaf key being the encoding of the tuple (keyID, key relationship) and the + // value being hte empty tuple + // A valid proof will contain a leaf with the key details for each reference + // leaf, with multiple reference leaves potentially referring to the same + // details leaf, as we already do with out `DidDetails` type. fn calculate_root_with_db(identity: &DidDetails, db: &mut MemoryDB) -> Result { use sp_trie::{TrieDBMutBuilder, TrieHash, TrieMut}; @@ -113,7 +129,7 @@ pub mod sender { trie_builder .insert(auth_leaf.encoded_key().as_slice(), auth_leaf.encoded_value().as_slice()) .map_err(|_| ())?; - // Attestation key + // Attestation key, if present if let Some(att_key_id) = identity.attestation_key { let att_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( KeyReferenceKey(att_key_id, DidVerificationKeyRelationship::AssertionMethod.into()), @@ -123,7 +139,7 @@ pub mod sender { .insert(att_leaf.encoded_key().as_slice(), att_leaf.encoded_value().as_slice()) .map_err(|_| ())?; }; - // Delegation key + // Delegation key, if present if let Some(del_key_id) = identity.delegation_key { let del_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( KeyReferenceKey(del_key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()), @@ -163,6 +179,10 @@ pub mod sender { } // TODO: Better error handling + // Only used for testing and as part of the features exposed by the runtime API + // of the provider. Given the provided `DidDetails` and a list of key IDs, it + // generates a merkle proof which only reveals the details of the provided key + // IDs. #[allow(clippy::result_unit_err)] pub fn generate_proof<'a, K>( identity: &DidDetails, @@ -181,6 +201,7 @@ pub mod sender { let leaves: BTreeSet, T::BlockNumber>> = key_ids.try_fold(BTreeSet::new(), |mut set, key_id| -> Result<_, ()> { let key_details = identity.public_keys.get(key_id).ok_or(())?; + // Adds a key reference leaf for each relationship the key ID is part of. if *key_id == identity.authentication_key { set.insert(ProofLeaf::KeyReference( KeyReferenceKey(*key_id, DidVerificationKeyRelationship::Authentication.into()), @@ -205,6 +226,9 @@ pub mod sender { KeyReferenceValue, )); }; + // Then adds the actual key details to the merkle proof. + // If the same key is specified twice, the old key is simply replaced with a new + // key of the same value. let key_details_leaf = ProofLeaf::KeyDetails(KeyDetailsKey(*key_id), KeyDetailsValue(key_details.clone())); if !set.contains(&key_details_leaf) { @@ -280,6 +304,8 @@ pub mod receiver { pub relationship: KeyRelationship, } + // Contains the list of revealed public keys after a given merkle proof has been + // correctly verified. #[derive(RuntimeDebug, PartialEq, Eq)] pub struct VerificationResult(pub Vec>); From 90d98db36e35c94354f8c8b7339659e08a14b684 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Mon, 27 Mar 2023 15:29:42 +0200 Subject: [PATCH 19/23] Fix runtime-benchmarks feature --- dip-template/runtimes/dip-receiver/Cargo.toml | 1 + dip-template/runtimes/dip-sender/Cargo.toml | 3 ++- runtimes/common/Cargo.toml | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/dip-template/runtimes/dip-receiver/Cargo.toml b/dip-template/runtimes/dip-receiver/Cargo.toml index b40065c863..76e495ad63 100644 --- a/dip-template/runtimes/dip-receiver/Cargo.toml +++ b/dip-template/runtimes/dip-receiver/Cargo.toml @@ -120,6 +120,7 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "frame-support/runtime-benchmarks", "pallet-dip-receiver/runtime-benchmarks", + "runtime-common/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", ] diff --git a/dip-template/runtimes/dip-sender/Cargo.toml b/dip-template/runtimes/dip-sender/Cargo.toml index f2a8ad9622..7e9bb84b86 100644 --- a/dip-template/runtimes/dip-sender/Cargo.toml +++ b/dip-template/runtimes/dip-sender/Cargo.toml @@ -119,9 +119,10 @@ std = [ ] runtime-benchmarks = [ "did/runtime-benchmarks", + "pallet-dip-sender/runtime-benchmarks", + "runtime-common/runtime-benchmarks", "frame-system/runtime-benchmarks", "frame-support/runtime-benchmarks", - "pallet-dip-sender/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "xcm-builder/runtime-benchmarks" ] diff --git a/runtimes/common/Cargo.toml b/runtimes/common/Cargo.toml index 8399e42faf..f2f7b16a8a 100644 --- a/runtimes/common/Cargo.toml +++ b/runtimes/common/Cargo.toml @@ -65,6 +65,8 @@ fast-gov = [] runtime-benchmarks = [ "attestation/runtime-benchmarks", "ctype/runtime-benchmarks", + "did/runtime-benchmarks", + "pallet-dip-receiver/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "kilt-support/runtime-benchmarks", @@ -112,7 +114,6 @@ std = [ try-runtime = [ "attestation/try-runtime", "delegation", - "did/try-runtime", "frame-support/try-runtime", "frame-system/try-runtime", "kilt-support/try-runtime", From 34e41150e3748dd6e94481005026f388bd651f47 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Mon, 27 Mar 2023 15:39:41 +0200 Subject: [PATCH 20/23] Remove MaxEncodedLen bound from DidPublicKey --- pallets/did/src/did_details.rs | 4 +--- runtime-api/did/src/did_details.rs | 2 +- runtime-api/did/src/lib.rs | 2 +- runtimes/common/src/dip.rs | 21 +++++++++------------ 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/pallets/did/src/did_details.rs b/pallets/did/src/did_details.rs index 6d9c2cdb0f..716b9e7171 100644 --- a/pallets/did/src/did_details.rs +++ b/pallets/did/src/did_details.rs @@ -235,9 +235,7 @@ impl> DidVerifiableIdentifier for I { /// It is currently used to keep track of all the past and current /// attestation keys a DID might control. #[derive(Clone, RuntimeDebug, Decode, Encode, PartialEq, Ord, PartialOrd, Eq, TypeInfo, MaxEncodedLen)] -#[scale_info(skip_type_params(T))] -#[codec(mel_bound())] -pub struct DidPublicKeyDetails { +pub struct DidPublicKeyDetails { /// A public key the DID controls. pub key: DidPublicKey, /// The block number in which the verification key was added to the DID. diff --git a/runtime-api/did/src/did_details.rs b/runtime-api/did/src/did_details.rs index 06ac8c089f..aa9b7adef5 100644 --- a/runtime-api/did/src/did_details.rs +++ b/runtime-api/did/src/did_details.rs @@ -24,7 +24,7 @@ use did::{did_details::DidPublicKeyDetails, AccountIdOf, BalanceOf, BlockNumberO use kilt_support::deposit::Deposit; #[derive(Encode, Decode, TypeInfo, Clone, Debug, Eq, PartialEq, MaxEncodedLen)] -pub struct DidDetails { +pub struct DidDetails { pub authentication_key: Key, pub key_agreement_keys: BTreeSet, pub delegation_key: Option, diff --git a/runtime-api/did/src/lib.rs b/runtime-api/did/src/lib.rs index 0231d618cd..a52176cee5 100644 --- a/runtime-api/did/src/lib.rs +++ b/runtime-api/did/src/lib.rs @@ -39,7 +39,7 @@ pub struct DidLinkedInfo< Url, Balance, Key: Ord, - BlockNumber: MaxEncodedLen, + BlockNumber, > { pub identifier: DidIdentifier, pub accounts: Vec, diff --git a/runtimes/common/src/dip.rs b/runtimes/common/src/dip.rs index 5c331cb263..2f87757160 100644 --- a/runtimes/common/src/dip.rs +++ b/runtimes/common/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 codec::{Decode, Encode, MaxEncodedLen}; +use codec::{Decode, Encode}; use did::{did_details::DidPublicKeyDetails, DidVerificationKeyRelationship}; use frame_support::RuntimeDebug; use scale_info::TypeInfo; @@ -42,10 +42,10 @@ 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); +pub struct KeyDetailsValue(pub DidPublicKeyDetails); #[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub enum ProofLeaf { +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), @@ -58,7 +58,7 @@ pub enum ProofLeaf { impl ProofLeaf where KeyId: Encode, - BlockNumber: MaxEncodedLen + Encode, + BlockNumber: Encode, { pub(crate) fn encoded_key(&self) -> Vec { match self { @@ -299,7 +299,7 @@ pub mod receiver { // 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)] - pub struct ProofEntry { + pub struct ProofEntry { pub key: DidPublicKeyDetails, pub relationship: KeyRelationship, } @@ -307,12 +307,9 @@ pub mod receiver { // Contains the list of revealed public keys after a given merkle proof has been // correctly verified. #[derive(RuntimeDebug, PartialEq, Eq)] - pub struct VerificationResult(pub Vec>); + pub struct VerificationResult(pub Vec>); - impl From>> for VerificationResult - where - BlockNumber: MaxEncodedLen, - { + impl From>> for VerificationResult { fn from(value: Vec>) -> Self { Self(value) } @@ -322,8 +319,8 @@ pub mod receiver { impl IdentityProofVerifier for DidMerkleProofVerifier where - KeyId: MaxEncodedLen + Clone + Ord, - BlockNumber: MaxEncodedLen + Clone + Ord, + KeyId: Encode + Clone + Ord, + BlockNumber: Encode + Clone + Ord, Hasher: sp_core::Hasher, { type BlindedValue = Vec; From bba72a216c1c950e739826b8edcbea04e0f866ec Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Tue, 28 Mar 2023 11:34:07 +0200 Subject: [PATCH 21/23] Reove imports from inside functions --- dip-template/runtimes/xcm-tests/src/para.rs | 16 +++++----------- dip-template/runtimes/xcm-tests/src/tests.rs | 5 ++--- runtimes/common/src/dip.rs | 16 ++++------------ 3 files changed, 11 insertions(+), 26 deletions(-) diff --git a/dip-template/runtimes/xcm-tests/src/para.rs b/dip-template/runtimes/xcm-tests/src/para.rs index 2649bf36ae..15fc36c469 100644 --- a/dip-template/runtimes/xcm-tests/src/para.rs +++ b/dip-template/runtimes/xcm-tests/src/para.rs @@ -24,8 +24,10 @@ use xcm_emulator::decl_test_parachain; pub(super) mod sender { pub(crate) use dip_sender_runtime_template::{DidIdentifier, DmpQueue, Runtime, RuntimeOrigin, XcmpQueue}; - use did::did_details::DidDetails; - use sp_core::{ed25519, Pair}; + use did::did_details::{DidDetails, DidEncryptionKey, DidVerificationKey}; + use dip_sender_runtime_template::{AccountId, System}; + use kilt_support::deposit::Deposit; + use sp_core::{ecdsa, ed25519, sr25519, Pair}; use super::*; @@ -36,11 +38,6 @@ pub(super) mod sender { } fn generate_did_details() -> DidDetails { - use did::did_details::{DidEncryptionKey, DidVerificationKey}; - use dip_sender_runtime_template::AccountId; - use kilt_support::deposit::Deposit; - use sp_core::{ecdsa, sr25519}; - let auth_key: DidVerificationKey = did_auth_key().public().into(); let att_key: DidVerificationKey = sr25519::Pair::from_seed(&[100u8; 32]).public().into(); let del_key: DidVerificationKey = ecdsa::Pair::from_seed(&[101u8; 32]).public().into(); @@ -63,8 +60,6 @@ pub(super) mod sender { } pub(crate) fn para_ext() -> TestExternalities { - use dip_sender_runtime_template::System; - let mut t = frame_system::GenesisConfig::default() .build_storage::() .unwrap(); @@ -102,6 +97,7 @@ pub(super) mod receiver { AccountId, AssetTransactorLocationConverter, Balance, DmpQueue, Runtime, RuntimeOrigin, XcmpQueue, UNIT, }; + use dip_receiver_runtime_template::System; use xcm::latest::{Junction::Parachain, Junctions::X1, ParentThen}; use xcm_executor::traits::Convert; @@ -117,8 +113,6 @@ pub(super) mod receiver { } pub(crate) fn para_ext() -> TestExternalities { - use dip_receiver_runtime_template::System; - let mut t = frame_system::GenesisConfig::default() .build_storage::() .unwrap(); diff --git a/dip-template/runtimes/xcm-tests/src/tests.rs b/dip-template/runtimes/xcm-tests/src/tests.rs index 4c6be6c740..e8cabc3997 100644 --- a/dip-template/runtimes/xcm-tests/src/tests.rs +++ b/dip-template/runtimes/xcm-tests/src/tests.rs @@ -32,8 +32,10 @@ use xcm::latest::{ }; use xcm_emulator::TestExt; +use cumulus_pallet_xcmp_queue::Event as XcmpEvent; use dip_receiver_runtime_template::{ DidIdentifier, DidLookup, DipReceiver, Runtime as ReceiverRuntime, RuntimeCall as ReceiverRuntimeCall, + RuntimeEvent, System, }; use dip_sender_runtime_template::{AccountId as SenderAccountId, DipSender, Runtime as SenderRuntime}; @@ -55,9 +57,6 @@ fn commit_identity() { }); // 2. Verify that the proof has made it to the DIP receiver. ReceiverParachain::execute_with(|| { - use cumulus_pallet_xcmp_queue::Event as XcmpEvent; - use dip_receiver_runtime_template::{RuntimeEvent, System}; - // 2.1 Verify that there was no XCM error. assert!(!System::events().iter().any(|r| matches!( r.event, diff --git a/runtimes/common/src/dip.rs b/runtimes/common/src/dip.rs index 2f87757160..395408296c 100644 --- a/runtimes/common/src/dip.rs +++ b/runtimes/common/src/dip.rs @@ -81,8 +81,8 @@ pub mod sender { use did::{did_details::DidDetails, KeyIdOf}; use dip_support::latest::Proof; use pallet_dip_sender::traits::{IdentityProofGenerator, IdentityProvider}; - use sp_std::borrow::ToOwned; - use sp_trie::{LayoutV1, MemoryDB}; + use sp_std::{borrow::ToOwned, collections::btree_set::BTreeSet}; + use sp_trie::{generate_trie_proof, LayoutV1, MemoryDB, TrieDBMutBuilder, TrieHash, TrieMut}; pub type BlindedValue = Vec; @@ -113,8 +113,6 @@ pub mod sender { // leaf, with multiple reference leaves potentially referring to the same // details leaf, as we already do with out `DidDetails` type. fn calculate_root_with_db(identity: &DidDetails, db: &mut MemoryDB) -> Result { - use sp_trie::{TrieDBMutBuilder, TrieHash, TrieMut}; - let mut trie = TrieHash::>::default(); let mut trie_builder = TrieDBMutBuilder::>::new(db, &mut trie).build(); @@ -191,9 +189,6 @@ pub mod sender { where K: Iterator>, { - use sp_std::collections::btree_set::BTreeSet; - use sp_trie::generate_trie_proof; - let mut db = MemoryDB::default(); let root = Self::calculate_root_with_db(identity, &mut db)?; @@ -291,10 +286,10 @@ pub mod sender { pub mod receiver { use super::*; - use dip_support::VersionedIdentityProof; + use dip_support::{v1, VersionedIdentityProof}; use pallet_dip_receiver::traits::IdentityProofVerifier; use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; - use sp_trie::LayoutV1; + use sp_trie::{verify_trie_proof, LayoutV1}; // TODO: Avoid repetition of the same key if it appears multiple times, e.g., by // having a vector of `KeyRelationship` instead. @@ -334,9 +329,6 @@ pub mod receiver { proof: VersionedIdentityProof, digest: Self::ProofDigest, ) -> Result { - use dip_support::v1; - use sp_trie::verify_trie_proof; - let proof: v1::Proof<_, _> = 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 From b33518c187ea0742fb3f2666043677ff15b1746f Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Tue, 28 Mar 2023 11:51:59 +0200 Subject: [PATCH 22/23] Split up dip module into submodules --- runtimes/common/src/dip.rs | 687 ---------------------------- runtimes/common/src/dip/mod.rs | 82 ++++ runtimes/common/src/dip/receiver.rs | 118 +++++ runtimes/common/src/dip/sender.rs | 221 +++++++++ runtimes/common/src/dip/tests.rs | 315 +++++++++++++ 5 files changed, 736 insertions(+), 687 deletions(-) delete mode 100644 runtimes/common/src/dip.rs create mode 100644 runtimes/common/src/dip/mod.rs create mode 100644 runtimes/common/src/dip/receiver.rs create mode 100644 runtimes/common/src/dip/sender.rs create mode 100644 runtimes/common/src/dip/tests.rs diff --git a/runtimes/common/src/dip.rs b/runtimes/common/src/dip.rs deleted file mode 100644 index 395408296c..0000000000 --- a/runtimes/common/src/dip.rs +++ /dev/null @@ -1,687 +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 codec::{Decode, Encode}; -use did::{did_details::DidPublicKeyDetails, DidVerificationKeyRelationship}; -use frame_support::RuntimeDebug; -use scale_info::TypeInfo; -use sp_std::{marker::PhantomData, vec::Vec}; - -#[derive(Clone, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord)] -pub enum KeyRelationship { - Encryption, - Verification(DidVerificationKeyRelationship), -} - -impl From for KeyRelationship { - fn from(value: DidVerificationKeyRelationship) -> Self { - Self::Verification(value) - } -} - -#[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 sender { - use super::*; - - use did::{did_details::DidDetails, KeyIdOf}; - use dip_support::latest::Proof; - use pallet_dip_sender::traits::{IdentityProofGenerator, IdentityProvider}; - use sp_std::{borrow::ToOwned, collections::btree_set::BTreeSet}; - use sp_trie::{generate_trie_proof, LayoutV1, MemoryDB, TrieDBMutBuilder, TrieHash, TrieMut}; - - pub type BlindedValue = Vec; - - pub type DidMerkleProof = - Proof, ProofLeaf, ::BlockNumber>>; - - #[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] - pub struct CompleteMerkleProof { - pub root: Root, - pub proof: Proof, - } - - pub struct DidMerkleRootGenerator(PhantomData); - - impl DidMerkleRootGenerator - where - T: did::Config, - { - // Calls the function in the `sp_trie` crate to generate the merkle root given - // the provided `DidDetails`. - // Each key in the merkle tree is added in the following way: - // - keys in the `public_keys` map are added by value in the merkle tree, with - // the leaf key being the key ID and the value being the key details - // - keys everywhere else in the DidDetails are added by reference, with the - // leaf key being the encoding of the tuple (keyID, key relationship) and the - // value being hte empty tuple - // A valid proof will contain a leaf with the key details for each reference - // leaf, with multiple reference leaves potentially referring to the same - // details leaf, as we already do with out `DidDetails` type. - fn calculate_root_with_db(identity: &DidDetails, db: &mut MemoryDB) -> Result { - let mut trie = TrieHash::>::default(); - let mut trie_builder = TrieDBMutBuilder::>::new(db, &mut trie).build(); - - // Authentication key - let auth_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( - KeyReferenceKey( - identity.authentication_key, - DidVerificationKeyRelationship::Authentication.into(), - ), - KeyReferenceValue, - ); - trie_builder - .insert(auth_leaf.encoded_key().as_slice(), auth_leaf.encoded_value().as_slice()) - .map_err(|_| ())?; - // Attestation key, if present - if let Some(att_key_id) = identity.attestation_key { - let att_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( - KeyReferenceKey(att_key_id, DidVerificationKeyRelationship::AssertionMethod.into()), - KeyReferenceValue, - ); - trie_builder - .insert(att_leaf.encoded_key().as_slice(), att_leaf.encoded_value().as_slice()) - .map_err(|_| ())?; - }; - // Delegation key, if present - if let Some(del_key_id) = identity.delegation_key { - let del_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( - KeyReferenceKey(del_key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()), - KeyReferenceValue, - ); - trie_builder - .insert(del_leaf.encoded_key().as_slice(), del_leaf.encoded_value().as_slice()) - .map_err(|_| ())?; - }; - // Key agreement keys - identity - .key_agreement_keys - .iter() - .try_for_each(|id| -> Result<(), ()> { - let enc_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( - KeyReferenceKey(*id, KeyRelationship::Encryption), - KeyReferenceValue, - ); - trie_builder - .insert(enc_leaf.encoded_key().as_slice(), enc_leaf.encoded_value().as_slice()) - .map_err(|_| ())?; - Ok(()) - })?; - // Public keys - identity - .public_keys - .iter() - .try_for_each(|(id, key_details)| -> Result<(), ()> { - let key_leaf = ProofLeaf::KeyDetails(KeyDetailsKey(*id), KeyDetailsValue(key_details.clone())); - trie_builder - .insert(key_leaf.encoded_key().as_slice(), key_leaf.encoded_value().as_slice()) - .map_err(|_| ())?; - Ok(()) - })?; - trie_builder.commit(); - Ok(trie_builder.root().to_owned()) - } - - // TODO: Better error handling - // Only used for testing and as part of the features exposed by the runtime API - // of the provider. Given the provided `DidDetails` and a list of key IDs, it - // generates a merkle proof which only reveals the details of the provided key - // IDs. - #[allow(clippy::result_unit_err)] - pub fn generate_proof<'a, K>( - identity: &DidDetails, - mut key_ids: K, - ) -> Result>, ()> - where - K: Iterator>, - { - let mut db = MemoryDB::default(); - let root = Self::calculate_root_with_db(identity, &mut db)?; - - #[allow(clippy::type_complexity)] - let leaves: BTreeSet, T::BlockNumber>> = - key_ids.try_fold(BTreeSet::new(), |mut set, key_id| -> Result<_, ()> { - let key_details = identity.public_keys.get(key_id).ok_or(())?; - // Adds a key reference leaf for each relationship the key ID is part of. - if *key_id == identity.authentication_key { - set.insert(ProofLeaf::KeyReference( - KeyReferenceKey(*key_id, DidVerificationKeyRelationship::Authentication.into()), - KeyReferenceValue, - )); - } - if Some(*key_id) == identity.attestation_key { - set.insert(ProofLeaf::KeyReference( - KeyReferenceKey(*key_id, DidVerificationKeyRelationship::AssertionMethod.into()), - KeyReferenceValue, - )); - } - if Some(*key_id) == identity.delegation_key { - set.insert(ProofLeaf::KeyReference( - KeyReferenceKey(*key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()), - KeyReferenceValue, - )); - } - if identity.key_agreement_keys.contains(key_id) { - set.insert(ProofLeaf::KeyReference( - KeyReferenceKey(*key_id, KeyRelationship::Encryption), - KeyReferenceValue, - )); - }; - // Then adds the actual key details to the merkle proof. - // If the same key is specified twice, the old key is simply replaced with a new - // key of the same value. - let key_details_leaf = - ProofLeaf::KeyDetails(KeyDetailsKey(*key_id), KeyDetailsValue(key_details.clone())); - if !set.contains(&key_details_leaf) { - set.insert(key_details_leaf); - } - Ok(set) - })?; - let encoded_keys: Vec> = leaves.iter().map(|l| l.encoded_key()).collect(); - let proof = - generate_trie_proof::, _, _, _>(&db, root, &encoded_keys).map_err(|_| ())?; - Ok(CompleteMerkleProof { - root, - proof: DidMerkleProof:: { - blinded: proof, - revealed: leaves.into_iter().collect::>(), - }, - }) - } - } - - impl IdentityProofGenerator> for DidMerkleRootGenerator - where - T: did::Config, - { - // TODO: Proper error handling - type Error = (); - type Output = T::Hash; - - fn generate_commitment( - _identifier: &T::DidIdentifier, - identity: &DidDetails, - ) -> Result { - let mut db = MemoryDB::default(); - 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(()), - } - } - } -} - -pub mod receiver { - use super::*; - - use dip_support::{v1, VersionedIdentityProof}; - use pallet_dip_receiver::traits::IdentityProofVerifier; - use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; - use sp_trie::{verify_trie_proof, LayoutV1}; - - // 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)] - pub struct ProofEntry { - pub key: DidPublicKeyDetails, - pub relationship: KeyRelationship, - } - - // Contains the list of revealed public keys after a given merkle proof has been - // correctly verified. - #[derive(RuntimeDebug, PartialEq, Eq)] - pub struct VerificationResult(pub Vec>); - - impl From>> for VerificationResult { - fn from(value: Vec>) -> Self { - Self(value) - } - } - - pub struct DidMerkleProofVerifier(PhantomData<(KeyId, BlockNumber, Hasher)>); - - 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 VerificationResult = VerificationResult; - - fn verify_proof_against_digest( - proof: VersionedIdentityProof, - digest: Self::ProofDigest, - ) -> Result { - let proof: v1::Proof<_, _> = 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>)>. - let proof_leaves = proof - .revealed - .iter() - .map(|leaf| (leaf.encoded_key(), Some(leaf.encoded_value()))) - .collect::, Option>)>>(); - verify_trie_proof::, _, _, _>(&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. - - // 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), KeyReferenceValue) = 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()) - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - use crate::dip::{ - receiver::DidMerkleProofVerifier, - sender::{CompleteMerkleProof, DidMerkleRootGenerator}, - }; - - use did::{ - did_details::{DidCreationDetails, DidEncryptionKey}, - KeyIdOf, - }; - use dip_support::latest::Proof; - 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_receiver::traits::IdentityProofVerifier; - 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; - - 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( - Proof { - blinded: proof.blinded, - revealed: reference_only_authentication_leaf - } - .into(), - root - ), - () - ); - }) - } -} diff --git a/runtimes/common/src/dip/mod.rs b/runtimes/common/src/dip/mod.rs new file mode 100644 index 0000000000..e8503d18cc --- /dev/null +++ b/runtimes/common/src/dip/mod.rs @@ -0,0 +1,82 @@ +// 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 codec::{Decode, Encode}; +use did::{did_details::DidPublicKeyDetails, DidVerificationKeyRelationship}; +use frame_support::RuntimeDebug; +use scale_info::TypeInfo; +use sp_std::vec::Vec; + +pub mod receiver; +pub mod sender; + +#[cfg(test)] +mod tests; + +#[derive(Clone, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord)] +pub enum KeyRelationship { + Encryption, + Verification(DidVerificationKeyRelationship), +} + +impl From for KeyRelationship { + fn from(value: DidVerificationKeyRelationship) -> Self { + Self::Verification(value) + } +} + +#[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(), + } + } +} diff --git a/runtimes/common/src/dip/receiver.rs b/runtimes/common/src/dip/receiver.rs new file mode 100644 index 0000000000..b4491b568b --- /dev/null +++ b/runtimes/common/src/dip/receiver.rs @@ -0,0 +1,118 @@ +// 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 codec::Encode; +use did::did_details::DidPublicKeyDetails; +use dip_support::{v1, VersionedIdentityProof}; +use frame_support::RuntimeDebug; +use pallet_dip_receiver::traits::IdentityProofVerifier; +use sp_std::{collections::btree_map::BTreeMap, marker::PhantomData, vec::Vec}; +use sp_trie::{verify_trie_proof, LayoutV1}; + +use crate::dip::{sender, 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)] +pub struct ProofEntry { + pub key: DidPublicKeyDetails, + pub relationship: KeyRelationship, +} + +// Contains the list of revealed public keys after a given merkle proof has been +// correctly verified. +#[derive(RuntimeDebug, PartialEq, Eq)] +pub struct VerificationResult(pub Vec>); + +impl From>> for VerificationResult { + fn from(value: Vec>) -> Self { + Self(value) + } +} + +pub struct DidMerkleProofVerifier(PhantomData<(KeyId, BlockNumber, Hasher)>); + +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 VerificationResult = VerificationResult; + + fn verify_proof_against_digest( + proof: VersionedIdentityProof, + digest: Self::ProofDigest, + ) -> Result { + let proof: v1::Proof<_, _> = 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>)>. + let proof_leaves = proof + .revealed + .iter() + .map(|leaf| (leaf.encoded_key(), Some(leaf.encoded_value()))) + .collect::, Option>)>>(); + verify_trie_proof::, _, _, _>(&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. + + // 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()) + } +} diff --git a/runtimes/common/src/dip/sender.rs b/runtimes/common/src/dip/sender.rs new file mode 100644 index 0000000000..f4534e12ba --- /dev/null +++ b/runtimes/common/src/dip/sender.rs @@ -0,0 +1,221 @@ +// 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 codec::{Decode, Encode}; +use did::{did_details::DidDetails, DidVerificationKeyRelationship, KeyIdOf}; +use dip_support::latest::Proof; +use frame_support::RuntimeDebug; +use pallet_dip_sender::traits::{IdentityProofGenerator, IdentityProvider}; +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}; + +pub type BlindedValue = Vec; + +pub type DidMerkleProof = Proof, ProofLeaf, ::BlockNumber>>; + +#[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] +pub struct CompleteMerkleProof { + pub root: Root, + pub proof: Proof, +} + +pub struct DidMerkleRootGenerator(PhantomData); + +impl DidMerkleRootGenerator +where + T: did::Config, +{ + // Calls the function in the `sp_trie` crate to generate the merkle root given + // the provided `DidDetails`. + // Each key in the merkle tree is added in the following way: + // - keys in the `public_keys` map are added by value in the merkle tree, with + // the leaf key being the key ID and the value being the key details + // - keys everywhere else in the DidDetails are added by reference, with the + // leaf key being the encoding of the tuple (keyID, key relationship) and the + // value being hte empty tuple + // A valid proof will contain a leaf with the key details for each reference + // leaf, with multiple reference leaves potentially referring to the same + // details leaf, as we already do with out `DidDetails` type. + fn calculate_root_with_db(identity: &DidDetails, db: &mut MemoryDB) -> Result { + let mut trie = TrieHash::>::default(); + let mut trie_builder = TrieDBMutBuilder::>::new(db, &mut trie).build(); + + // Authentication key + let auth_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( + KeyReferenceKey( + identity.authentication_key, + DidVerificationKeyRelationship::Authentication.into(), + ), + KeyReferenceValue, + ); + trie_builder + .insert(auth_leaf.encoded_key().as_slice(), auth_leaf.encoded_value().as_slice()) + .map_err(|_| ())?; + // Attestation key, if present + if let Some(att_key_id) = identity.attestation_key { + let att_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( + KeyReferenceKey(att_key_id, DidVerificationKeyRelationship::AssertionMethod.into()), + KeyReferenceValue, + ); + trie_builder + .insert(att_leaf.encoded_key().as_slice(), att_leaf.encoded_value().as_slice()) + .map_err(|_| ())?; + }; + // Delegation key, if present + if let Some(del_key_id) = identity.delegation_key { + let del_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( + KeyReferenceKey(del_key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()), + KeyReferenceValue, + ); + trie_builder + .insert(del_leaf.encoded_key().as_slice(), del_leaf.encoded_value().as_slice()) + .map_err(|_| ())?; + }; + // Key agreement keys + identity + .key_agreement_keys + .iter() + .try_for_each(|id| -> Result<(), ()> { + let enc_leaf = ProofLeaf::<_, T::BlockNumber>::KeyReference( + KeyReferenceKey(*id, KeyRelationship::Encryption), + KeyReferenceValue, + ); + trie_builder + .insert(enc_leaf.encoded_key().as_slice(), enc_leaf.encoded_value().as_slice()) + .map_err(|_| ())?; + Ok(()) + })?; + // Public keys + identity + .public_keys + .iter() + .try_for_each(|(id, key_details)| -> Result<(), ()> { + let key_leaf = ProofLeaf::KeyDetails(KeyDetailsKey(*id), KeyDetailsValue(key_details.clone())); + trie_builder + .insert(key_leaf.encoded_key().as_slice(), key_leaf.encoded_value().as_slice()) + .map_err(|_| ())?; + Ok(()) + })?; + trie_builder.commit(); + Ok(trie_builder.root().to_owned()) + } + + // TODO: Better error handling + // Only used for testing and as part of the features exposed by the runtime API + // of the provider. Given the provided `DidDetails` and a list of key IDs, it + // generates a merkle proof which only reveals the details of the provided key + // IDs. + #[allow(clippy::result_unit_err)] + pub fn generate_proof<'a, K>( + identity: &DidDetails, + mut key_ids: K, + ) -> Result>, ()> + where + K: Iterator>, + { + let mut db = MemoryDB::default(); + let root = Self::calculate_root_with_db(identity, &mut db)?; + + #[allow(clippy::type_complexity)] + let leaves: BTreeSet, T::BlockNumber>> = + key_ids.try_fold(BTreeSet::new(), |mut set, key_id| -> Result<_, ()> { + let key_details = identity.public_keys.get(key_id).ok_or(())?; + // Adds a key reference leaf for each relationship the key ID is part of. + if *key_id == identity.authentication_key { + set.insert(ProofLeaf::KeyReference( + KeyReferenceKey(*key_id, DidVerificationKeyRelationship::Authentication.into()), + KeyReferenceValue, + )); + } + if Some(*key_id) == identity.attestation_key { + set.insert(ProofLeaf::KeyReference( + KeyReferenceKey(*key_id, DidVerificationKeyRelationship::AssertionMethod.into()), + KeyReferenceValue, + )); + } + if Some(*key_id) == identity.delegation_key { + set.insert(ProofLeaf::KeyReference( + KeyReferenceKey(*key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()), + KeyReferenceValue, + )); + } + if identity.key_agreement_keys.contains(key_id) { + set.insert(ProofLeaf::KeyReference( + KeyReferenceKey(*key_id, KeyRelationship::Encryption), + KeyReferenceValue, + )); + }; + // Then adds the actual key details to the merkle proof. + // If the same key is specified twice, the old key is simply replaced with a new + // key of the same value. + let key_details_leaf = + ProofLeaf::KeyDetails(KeyDetailsKey(*key_id), KeyDetailsValue(key_details.clone())); + if !set.contains(&key_details_leaf) { + set.insert(key_details_leaf); + } + Ok(set) + })?; + let encoded_keys: Vec> = leaves.iter().map(|l| l.encoded_key()).collect(); + let proof = generate_trie_proof::, _, _, _>(&db, root, &encoded_keys).map_err(|_| ())?; + Ok(CompleteMerkleProof { + root, + proof: DidMerkleProof:: { + blinded: proof, + revealed: leaves.into_iter().collect::>(), + }, + }) + } +} + +impl IdentityProofGenerator> for DidMerkleRootGenerator +where + T: did::Config, +{ + // TODO: Proper error handling + type Error = (); + type Output = T::Hash; + + fn generate_commitment(_identifier: &T::DidIdentifier, identity: &DidDetails) -> Result { + let mut db = MemoryDB::default(); + 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/tests.rs b/runtimes/common/src/dip/tests.rs new file mode 100644 index 0000000000..65242bd98c --- /dev/null +++ b/runtimes/common/src/dip/tests.rs @@ -0,0 +1,315 @@ +// 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 codec::Encode; +use did::{ + did_details::{DidCreationDetails, DidEncryptionKey}, + DidVerificationKeyRelationship, KeyIdOf, +}; +use dip_support::latest::Proof; +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_receiver::traits::IdentityProofVerifier; +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::{ + receiver::DidMerkleProofVerifier, + sender::{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( + Proof { + blinded: proof.blinded, + revealed: reference_only_authentication_leaf + } + .into(), + root + ), + () + ); + }) +} From ebe454d8c6391e9b693d90fc00347708a6e3c7c0 Mon Sep 17 00:00:00 2001 From: Antonio Antonino Date: Wed, 5 Apr 2023 09:42:14 +0200 Subject: [PATCH 23/23] Make VersionedIdentity* non_exhaustive --- crates/dip/src/lib.rs | 2 ++ pallets/pallet-dip-receiver/src/lib.rs | 8 +++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/dip/src/lib.rs b/crates/dip/src/lib.rs index a9b83ac39c..15c6a58109 100644 --- a/crates/dip/src/lib.rs +++ b/crates/dip/src/lib.rs @@ -31,6 +31,7 @@ pub mod latest { } #[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] +#[non_exhaustive] pub enum VersionedIdentityProofAction { #[codec(index = 1)] V1(v1::IdentityProofAction), @@ -45,6 +46,7 @@ impl From { #[codec(index = 1)] V1(v1::Proof), diff --git a/pallets/pallet-dip-receiver/src/lib.rs b/pallets/pallet-dip-receiver/src/lib.rs index 4c33ab8fae..cb93b72281 100644 --- a/pallets/pallet-dip-receiver/src/lib.rs +++ b/pallets/pallet-dip-receiver/src/lib.rs @@ -84,6 +84,7 @@ pub mod pallet { Dispatch, IdentityNotFound, InvalidProof, + UnsupportedVersion, } // The new origin other pallets can use. @@ -104,13 +105,14 @@ pub mod pallet { let event = match action { VersionedIdentityProofAction::V1(IdentityProofAction::Updated(identifier, proof, _)) => { IdentityProofs::::mutate(&identifier, |entry| *entry = Some(proof.clone())); - Event::::IdentityInfoUpdated(identifier, proof) + Ok::<_, Error>(Event::::IdentityInfoUpdated(identifier, proof)) } VersionedIdentityProofAction::V1(IdentityProofAction::Deleted(identifier)) => { IdentityProofs::::remove(&identifier); - Event::::IdentityInfoDeleted(identifier) + Ok::<_, Error>(Event::::IdentityInfoDeleted(identifier)) } - }; + _ => Err(Error::::UnsupportedVersion), + }?; Self::deposit_event(event);