From 62a1373d20cae10a72e67973f5f6392052a6eaee Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 22 Feb 2024 11:07:02 +0300 Subject: [PATCH] chore: DIP improvements (#602) Fixes /~https://github.com/KILTprotocol/ticket/issues/3054. It also contains a **breaking change** for cross-chain DID signature verifications. The signature now has a `valid_until` field, as discussed in /~https://github.com/KILTprotocol/kilt-node/pull/494#discussion_r1417201933. Besides that, other changes include: * A new set of types representing different stages of a cross-chain DIP proof, during the verification process. Everything starts with either a `RelayDipDidProof` or a `ParachainDipDidProof` and ends, if the whole verification flow succeeds, with a `DipVerifiedInfo`. * A generic `verify_storage_value_proof` that is used to verify a single storage element with a storage proof. * The `KiltVersionedParachainVerifier` now also depends on the relaychain runtime, which removes the need for some traits that provided just type definitions, such as `RelayChainStorageInfo` and `RelayChainStateInfo`. * Errors conversions into `u8` now start from `1` instead of `0`, for disambiguating between an error returning `u8::MAX` and another case returning `u8::MAX + 0`. * Refactoring of the different modules. **No unit tests or benchmarks yet, but this code should make it easier to do all of those**. --- Cargo.lock | 21 +- crates/kilt-dip-primitives/Cargo.toml | 13 - crates/kilt-dip-primitives/src/did.rs | 223 --- crates/kilt-dip-primitives/src/lib.rs | 12 +- crates/kilt-dip-primitives/src/merkle.rs | 416 ------ crates/kilt-dip-primitives/src/merkle/mod.rs | 23 + crates/kilt-dip-primitives/src/merkle/v0.rs | 1303 +++++++++++++++++ .../src/state_proofs/mod.rs | 215 ++- .../src/state_proofs/parachain.rs | 168 --- .../src/state_proofs/relaychain.rs | 261 ---- .../src/state_proofs/substrate_no_std_port.rs | 69 + crates/kilt-dip-primitives/src/traits.rs | 180 +-- crates/kilt-dip-primitives/src/utils.rs | 30 +- .../src/verifier/common.rs | 77 - .../kilt-dip-primitives/src/verifier/mod.rs | 7 - .../src/verifier/parachain.rs | 960 +++++------- .../src/verifier/relaychain.rs | 978 ++++--------- dip-template/nodes/dip-consumer/Cargo.toml | 1 - dip-template/nodes/dip-provider/Cargo.toml | 1 - dip-template/runtimes/dip-consumer/Cargo.toml | 23 +- dip-template/runtimes/dip-consumer/src/dip.rs | 52 +- .../dip-consumer/src/origin_adapter.rs | 13 +- dip-template/runtimes/dip-provider/Cargo.toml | 12 +- dip-template/runtimes/dip-provider/src/dip.rs | 6 +- nodes/parachain/src/chain_spec/clone.rs | 8 +- pallets/did/src/did_details.rs | 8 +- pallets/pallet-deposit-storage/src/deposit.rs | 12 +- pallets/pallet-dip-consumer/Cargo.toml | 6 +- pallets/pallet-dip-provider/Cargo.toml | 4 +- runtimes/clone/src/lib.rs | 2 +- runtimes/common/src/dip/did.rs | 12 +- runtimes/common/src/dip/merkle.rs | 356 ++--- runtimes/peregrine/src/dip/deposit.rs | 6 +- 33 files changed, 2554 insertions(+), 2924 deletions(-) delete mode 100644 crates/kilt-dip-primitives/src/did.rs delete mode 100644 crates/kilt-dip-primitives/src/merkle.rs create mode 100644 crates/kilt-dip-primitives/src/merkle/mod.rs create mode 100644 crates/kilt-dip-primitives/src/merkle/v0.rs delete mode 100644 crates/kilt-dip-primitives/src/state_proofs/parachain.rs delete mode 100644 crates/kilt-dip-primitives/src/state_proofs/relaychain.rs create mode 100644 crates/kilt-dip-primitives/src/state_proofs/substrate_no_std_port.rs delete mode 100644 crates/kilt-dip-primitives/src/verifier/common.rs diff --git a/Cargo.lock b/Cargo.lock index 1ba4cd276..fc112de48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2448,7 +2448,6 @@ dependencies = [ "jsonrpsee", "log", "pallet-transaction-payment-rpc", - "parity-scale-codec", "polkadot-cli", "polkadot-primitives", "sc-basic-authorship", @@ -2486,11 +2485,9 @@ name = "dip-consumer-runtime-template" version = "1.12.0-dev" dependencies = [ "cumulus-pallet-aura-ext", - "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-primitives-core", "cumulus-primitives-timestamp", - "cumulus-primitives-utility", "did", "dip-provider-runtime-template", "frame-benchmarking", @@ -2499,13 +2496,11 @@ dependencies = [ "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", - "hex-literal 0.3.4", "kilt-dip-primitives", "pallet-aura", "pallet-authorship", "pallet-balances", "pallet-collator-selection", - "pallet-did-lookup", "pallet-dip-consumer", "pallet-postit", "pallet-relay-store", @@ -2517,8 +2512,7 @@ dependencies = [ "pallet-utility", "parachain-info", "parity-scale-codec", - "polkadot-parachain", - "runtime-common", + "rococo-runtime", "scale-info", "sp-api", "sp-block-builder", @@ -2554,7 +2548,6 @@ dependencies = [ "jsonrpsee", "log", "pallet-transaction-payment-rpc", - "parity-scale-codec", "polkadot-cli", "polkadot-primitives", "sc-basic-authorship", @@ -2592,11 +2585,9 @@ name = "dip-provider-runtime-template" version = "1.12.0-dev" dependencies = [ "cumulus-pallet-aura-ext", - "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-primitives-core", "cumulus-primitives-timestamp", - "cumulus-primitives-utility", "did", "frame-benchmarking", "frame-executive", @@ -2604,11 +2595,8 @@ dependencies = [ "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", - "hex-literal 0.3.4", - "kilt-dip-primitives", "kilt-runtime-api-did", "kilt-runtime-api-dip-provider", - "kilt-support", "log", "pallet-aura", "pallet-authorship", @@ -4614,7 +4602,6 @@ name = "kilt-dip-primitives" version = "1.12.0-dev" dependencies = [ "cfg-if", - "cumulus-pallet-parachain-system", "cumulus-primitives-core", "did", "frame-support", @@ -4628,9 +4615,7 @@ dependencies = [ "pallet-dip-provider", "pallet-relay-store", "pallet-web3-names", - "parachain-info", "parity-scale-codec", - "rococo-runtime", "scale-info", "sp-core", "sp-io", @@ -4638,8 +4623,6 @@ dependencies = [ "sp-state-machine", "sp-std", "sp-trie", - "xcm", - "xcm-executor", ] [[package]] @@ -6600,7 +6583,6 @@ dependencies = [ "kilt-support", "parity-scale-codec", "scale-info", - "sp-core", "sp-io", "sp-keystore", "sp-runtime", @@ -6618,7 +6600,6 @@ dependencies = [ "kilt-support", "parity-scale-codec", "scale-info", - "sp-core", "sp-io", "sp-keystore", "sp-runtime", diff --git a/crates/kilt-dip-primitives/Cargo.toml b/crates/kilt-dip-primitives/Cargo.toml index 2afec4987..2b7f4adce 100644 --- a/crates/kilt-dip-primitives/Cargo.toml +++ b/crates/kilt-dip-primitives/Cargo.toml @@ -39,15 +39,8 @@ sp-state-machine.workspace = true sp-std.workspace = true sp-trie.workspace = true -# Polkadot dependencies -rococo-runtime.workspace = true -xcm.workspace = true -xcm-executor.workspace = true - # Cumulus dependencies -cumulus-pallet-parachain-system.workspace = true cumulus-primitives-core.workspace = true -parachain-info.workspace = true [dev-dependencies] hex-literal.workspace = true @@ -75,16 +68,10 @@ std = [ "sp-state-machine/std", "sp-std/std", "sp-trie/std", - "rococo-runtime/std", - "xcm/std", - "xcm-executor/std", - "cumulus-pallet-parachain-system/std", "cumulus-primitives-core/std", - "parachain-info/std", ] runtime-benchmarks = [ "kilt-support/runtime-benchmarks", "pallet-dip-consumer/runtime-benchmarks", "pallet-dip-provider/runtime-benchmarks", - "rococo-runtime/runtime-benchmarks" ] diff --git a/crates/kilt-dip-primitives/src/did.rs b/crates/kilt-dip-primitives/src/did.rs deleted file mode 100644 index 9f16bd298..000000000 --- a/crates/kilt-dip-primitives/src/did.rs +++ /dev/null @@ -1,223 +0,0 @@ -// KILT Blockchain – https://botlabs.org -// Copyright (C) 2019-2024 BOTLabs GmbH - -// The KILT Blockchain is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// The KILT Blockchain is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// If you feel like getting in touch with us, you can do so at info@botlabs.org - -//! Module to deal with cross-chain KILT DIDs. - -use did::{ - did_details::{DidPublicKey, DidPublicKeyDetails, DidVerificationKey}, - DidSignature, DidVerificationKeyRelationship, -}; -use parity_scale_codec::{Decode, Encode}; -use scale_info::TypeInfo; -use sp_core::RuntimeDebug; -use sp_runtime::traits::CheckedSub; -use sp_std::vec::Vec; - -use crate::{ - merkle::RevealedDidKey, - traits::{DidSignatureVerifierContext, DipCallOriginFilter, Incrementable}, -}; - -/// Type returned by the Merkle proof verifier component of the DIP consumer -/// after verifying a DIP Merkle proof. -#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)] -pub(crate) struct RevealedDidKeysAndSignature { - /// The keys revelaed in the Merkle proof. - pub merkle_leaves: RevealedDidKeys, - /// The [`DIDSignature`] + consumer chain block number to which the DID - /// signature is anchored. - pub did_signature: TimeBoundDidSignature, -} - -/// A DID signature anchored to a specific block height. -#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)] -pub struct TimeBoundDidSignature { - /// The signature. - pub signature: DidSignature, - /// The block number, in the context of the local executor, to which the - /// signature is anchored. - pub block_number: BlockNumber, -} - -#[cfg(feature = "runtime-benchmarks")] -impl kilt_support::traits::GetWorstCase for TimeBoundDidSignature -where - DidSignature: kilt_support::traits::GetWorstCase, - BlockNumber: Default, -{ - fn worst_case(context: Context) -> Self { - Self { - signature: DidSignature::worst_case(context), - block_number: BlockNumber::default(), - } - } -} - -pub enum RevealedDidKeysSignatureAndCallVerifierError { - SignatureNotFresh, - SignatureUnverifiable, - OriginCheckFailed, - Internal, -} - -impl From for u8 { - fn from(value: RevealedDidKeysSignatureAndCallVerifierError) -> Self { - match value { - RevealedDidKeysSignatureAndCallVerifierError::SignatureNotFresh => 0, - RevealedDidKeysSignatureAndCallVerifierError::SignatureUnverifiable => 1, - RevealedDidKeysSignatureAndCallVerifierError::OriginCheckFailed => 2, - RevealedDidKeysSignatureAndCallVerifierError::Internal => u8::MAX, - } - } -} - -/// Function that tries to verify a DID signature over a given payload by -/// using one of the DID keys revealed in the Merkle proof. This verifier is -/// typically used in conjunction with a verifier that takes a user-provided -/// input Merkle proof, verifies it, and transforms it into a struct that this -/// and other verifiers can easily consume, e.g., a list of DID keys. -/// The generic types are the following: -/// * `Call`: The call to be dispatched on the local chain after verifying the -/// DID signature. -/// * `Submitter`: The blockchain account (**not** the identity subject) -/// submitting the cross-chain transaction (and paying for its execution -/// fees). -/// * `DidLocalDetails`: Any information associated to the identity subject that -/// is stored locally, e.g., under the `IdentityEntries` map of the -/// `pallet-dip-consumer` pallet. -/// * `MerkleProofEntries`: The type returned by the Merkle proof verifier that -/// includes the identity parts revealed in the Merkle proof. -/// * `ContextProvider`: Provides additional local context (e.g., current block -/// number) to verify the DID signature. -/// * `RemoteKeyId`: Definition of a DID key ID as specified by the provider. -/// * `RemoteAccountId`: Definition of a linked account ID as specified by the -/// provider. -/// * `RemoteBlockNumber`: Definition of a block number on the provider chain. -/// * `CallVerifier`: A type specifying whether the provided `Call` can be -/// dispatched with the information provided in the DIP proof. -pub(crate) fn verify_did_signature_for_call< - Call, - Submitter, - DidLocalDetails, - MerkleProofEntries, - ContextProvider, - RemoteKeyId, - RemoteAccountId, - RemoteBlockNumber, - CallVerifier, ->( - call: &Call, - submitter: &Submitter, - local_details: &mut Option, - merkle_revealed_did_signature: RevealedDidKeysAndSignature, -) -> Result< - (DidVerificationKey, DidVerificationKeyRelationship), - RevealedDidKeysSignatureAndCallVerifierError, -> -where - Call: Encode, - Submitter: Encode, - ContextProvider: DidSignatureVerifierContext, - ContextProvider::BlockNumber: Encode + CheckedSub + From + PartialOrd, - ContextProvider::Hash: Encode, - ContextProvider::SignedExtra: Encode, - DidLocalDetails: Incrementable + Default + Encode, - RemoteAccountId: Clone, - MerkleProofEntries: sp_std::borrow::Borrow<[RevealedDidKey]>, - CallVerifier: - DipCallOriginFilter, DidVerificationKeyRelationship)>, -{ - cfg_if::cfg_if! { - if #[cfg(feature = "runtime-benchmarks")] { - {} - } else { - let block_number = ContextProvider::current_block_number(); - let is_signature_fresh = if let Some(blocks_ago_from_now) = - block_number.checked_sub(&merkle_revealed_did_signature.did_signature.block_number) - { - // False if the signature is too old. - blocks_ago_from_now <= ContextProvider::SIGNATURE_VALIDITY.into() - } else { - // Signature generated at a future time, not possible to verify. - false - }; - frame_support::ensure!( - is_signature_fresh, - RevealedDidKeysSignatureAndCallVerifierError::SignatureNotFresh, - ); - } - } - let encoded_payload = ( - call, - &local_details, - submitter, - &merkle_revealed_did_signature.did_signature.block_number, - ContextProvider::genesis_hash(), - ContextProvider::signed_extra(), - ) - .encode(); - // Only consider verification keys from the set of revealed keys. - let proof_verification_keys: Vec<(DidVerificationKey, DidVerificationKeyRelationship)> = merkle_revealed_did_signature.merkle_leaves.borrow().iter().filter_map(|RevealedDidKey { - relationship, details: DidPublicKeyDetails { key, .. }, .. } | { - let DidPublicKey::PublicVerificationKey(key) = key else { return None }; - if let Ok(vr) = DidVerificationKeyRelationship::try_from(*relationship) { - // TODO: Fix this logic to avoid cloning - Some(Ok((key.clone(), vr))) - } else { - cfg_if::cfg_if! { - if #[cfg(feature = "runtime-benchmarks")] { - None - } else { - log::error!("Should never fail to build a VerificationRelationship from the given DidKeyRelationship because we have already made sure the conditions hold."); - Some(Err(RevealedDidKeysSignatureAndCallVerifierError::Internal)) - } - } - } - }).collect::>()?; - let valid_signing_key = proof_verification_keys.iter().find(|(verification_key, _)| { - verification_key - .verify_signature(&encoded_payload, &merkle_revealed_did_signature.did_signature.signature) - .is_ok() - }); - cfg_if::cfg_if! { - if #[cfg(feature = "runtime-benchmarks")] { - let default = ( - DidVerificationKey::Ed25519(sp_core::ed25519::Public::from_raw([0u8; 32])), - DidVerificationKeyRelationship::Authentication, - ); - let (key, relationship) = valid_signing_key.unwrap_or(&default); - } else { - let (key, relationship) = valid_signing_key.ok_or(RevealedDidKeysSignatureAndCallVerifierError::SignatureUnverifiable)?; - } - } - - if let Some(details) = local_details { - details.increment(); - } else { - *local_details = Some(DidLocalDetails::default()); - }; - let res = CallVerifier::check_call_origin_info(call, &(key.clone(), *relationship)); - cfg_if::cfg_if! { - if #[cfg(feature = "runtime-benchmarks")] { - drop(res); - } else { - res.map_err(|_| RevealedDidKeysSignatureAndCallVerifierError::OriginCheckFailed)?; - } - } - Ok((key.clone(), *relationship)) -} diff --git a/crates/kilt-dip-primitives/src/lib.rs b/crates/kilt-dip-primitives/src/lib.rs index fc99b1d5e..30623e96c 100644 --- a/crates/kilt-dip-primitives/src/lib.rs +++ b/crates/kilt-dip-primitives/src/lib.rs @@ -26,14 +26,18 @@ #![cfg_attr(not(feature = "std"), no_std)] -pub mod did; +/// Module to deal with cross-chain Merkle proof as generated by the KILT chain. pub mod merkle; +/// Module to deal with cross-chain state proofs. pub mod state_proofs; +/// Collection of traits used throughout the crate and useful for both providers +/// and consumers. pub mod traits; pub mod utils; +/// Verifier module containing types that implement the verifier component to be +/// deployed both on a sibling parachain and on a parent relaychain. pub mod verifier; -pub use state_proofs::relaychain::RelayStateRootsViaRelayStorePallet; -pub use traits::{FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet}; -pub use utils::BoundedBlindedValue; +pub use merkle::latest::*; +pub use traits::RelayStateRootsViaRelayStorePallet; pub use verifier::*; diff --git a/crates/kilt-dip-primitives/src/merkle.rs b/crates/kilt-dip-primitives/src/merkle.rs deleted file mode 100644 index 65d2d90d8..000000000 --- a/crates/kilt-dip-primitives/src/merkle.rs +++ /dev/null @@ -1,416 +0,0 @@ -// KILT Blockchain – https://botlabs.org -// Copyright (C) 2019-2024 BOTLabs GmbH - -// The KILT Blockchain is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// The KILT Blockchain is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// If you feel like getting in touch with us, you can do so at info@botlabs.org - -//! Module to deal with cross-chain Merkle proof as generated by the KILT chain. - -use did::{did_details::DidPublicKeyDetails, DidVerificationKeyRelationship}; -use frame_support::{traits::ConstU32, DefaultNoBound, RuntimeDebug}; -use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; -use sp_runtime::{BoundedVec, SaturatedConversion}; -use sp_std::{fmt::Debug, vec::Vec}; -use sp_trie::{verify_trie_proof, LayoutV1}; - -/// Type of a Merkle proof containing DID-related information. -#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, Default, TypeInfo)] -pub struct DidMerkleProof { - pub blinded: BlindedValues, - // TODO: Probably replace with a different data structure for better lookup capabilities - pub revealed: Vec, -} - -#[cfg(feature = "runtime-benchmarks")] -impl kilt_support::traits::GetWorstCase for DidMerkleProof -where - BlindedValues: kilt_support::traits::GetWorstCase, - Leaf: Default + Clone, -{ - fn worst_case(context: Context) -> Self { - Self { - blinded: BlindedValues::worst_case(context), - revealed: sp_std::vec![Leaf::default(); 64], - } - } -} - -/// Relationship of a key to a DID Document. -#[derive(Clone, Copy, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, PartialOrd, Ord, MaxEncodedLen)] -pub enum DidKeyRelationship { - Encryption, - Verification(DidVerificationKeyRelationship), -} - -impl From for DidKeyRelationship { - fn from(value: DidVerificationKeyRelationship) -> Self { - Self::Verification(value) - } -} - -impl TryFrom for DidVerificationKeyRelationship { - type Error = (); - - fn try_from(value: DidKeyRelationship) -> Result { - if let DidKeyRelationship::Verification(rel) = value { - Ok(rel) - } else { - Err(()) - } - } -} - -/// The key of a Merkle leaf revealing a DID key for a DID Document. -#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct DidKeyMerkleKey(pub KeyId, pub DidKeyRelationship); - -impl From<(KeyId, DidKeyRelationship)> for DidKeyMerkleKey { - fn from(value: (KeyId, DidKeyRelationship)) -> Self { - Self(value.0, value.1) - } -} -/// The value of a Merkle leaf revealing a DID key for a DID Document. -#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct DidKeyMerkleValue(pub DidPublicKeyDetails); - -impl From> - for DidKeyMerkleValue -{ - fn from(value: DidPublicKeyDetails) -> Self { - Self(value) - } -} - -/// The key of a Merkle leaf revealing the web3name linked to a DID Document. -#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct Web3NameMerkleKey(pub Web3Name); - -impl From for Web3NameMerkleKey { - fn from(value: Web3Name) -> Self { - Self(value) - } -} -/// The value of a Merkle leaf revealing the web3name linked to a DID Document. -#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct Web3NameMerkleValue(pub BlockNumber); - -impl From for Web3NameMerkleValue { - fn from(value: BlockNumber) -> Self { - Self(value) - } -} - -/// The key of a Merkle leaf revealing an account linked to a DID Document. -#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct LinkedAccountMerkleKey(pub AccountId); - -impl From for LinkedAccountMerkleKey { - fn from(value: AccountId) -> Self { - Self(value) - } -} -/// The value of a Merkle leaf revealing an account linked to a DID -/// Document. -#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct LinkedAccountMerkleValue; - -impl From<()> for LinkedAccountMerkleValue { - fn from(_value: ()) -> Self { - Self - } -} - -/// All possible Merkle leaf types that can be revealed as part of a DIP -/// identity Merkle proof. -#[derive(Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub enum RevealedDidMerkleProofLeaf { - DidKey(DidKeyMerkleKey, DidKeyMerkleValue), - Web3Name(Web3NameMerkleKey, Web3NameMerkleValue), - LinkedAccount(LinkedAccountMerkleKey, LinkedAccountMerkleValue), -} - -#[cfg(feature = "runtime-benchmarks")] -impl Default - for RevealedDidMerkleProofLeaf -where - KeyId: Default, - BlockNumber: Default, -{ - fn default() -> Self { - Self::DidKey( - (KeyId::default(), DidVerificationKeyRelationship::Authentication.into()).into(), - DidPublicKeyDetails { - key: did::did_details::DidVerificationKey::Ed25519(sp_core::ed25519::Public::from_raw([0u8; 32])) - .into(), - block_number: BlockNumber::default(), - } - .into(), - ) - } -} - -impl - RevealedDidMerkleProofLeaf -where - KeyId: Encode, - Web3Name: Encode, - LinkedAccountId: Encode, -{ - pub fn encoded_key(&self) -> Vec { - match self { - RevealedDidMerkleProofLeaf::DidKey(key, _) => key.encode(), - RevealedDidMerkleProofLeaf::Web3Name(key, _) => key.encode(), - RevealedDidMerkleProofLeaf::LinkedAccount(key, _) => key.encode(), - } - } -} - -impl - RevealedDidMerkleProofLeaf -where - AccountId: Encode, - BlockNumber: Encode, -{ - pub fn encoded_value(&self) -> Vec { - match self { - RevealedDidMerkleProofLeaf::DidKey(_, value) => value.encode(), - RevealedDidMerkleProofLeaf::Web3Name(_, value) => value.encode(), - RevealedDidMerkleProofLeaf::LinkedAccount(_, value) => value.encode(), - } - } -} - -/// The details of a DID key after it has been successfully verified in a Merkle -/// proof. -#[derive(Clone, Encode, Decode, PartialEq, MaxEncodedLen, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct RevealedDidKey { - /// The key ID, according to the provider's definition. - pub id: KeyId, - /// The key relationship to the subject's DID Document. - pub relationship: DidKeyRelationship, - /// The details of the DID Key, including its creation block number on the - /// provider chain. - pub details: DidPublicKeyDetails, -} - -/// The details of a web3name after it has been successfully verified in a -/// Merkle proof. -#[derive(Clone, Encode, Decode, PartialEq, MaxEncodedLen, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo)] -pub struct RevealedWeb3Name { - /// The web3name. - pub web3_name: Web3Name, - /// The block number on the provider chain in which it was linked to the DID - /// subject. - pub claimed_at: BlockNumber, -} - -/// The complete set of information that is provided by the DIP Merkle proof -/// verifier upon successful verification of a DIP Merkle proof. -#[derive(Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen, Encode, Decode, DefaultNoBound)] -pub struct RevealedDidMerkleProofLeaves< - KeyId, - AccountId, - BlockNumber, - Web3Name, - LinkedAccountId, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, -> { - /// The list of [`RevealedDidKey`]s revealed in the Merkle proof, up to a - /// maximum of `MAX_REVEALED_KEYS_COUNT`. - pub did_keys: BoundedVec, ConstU32>, - /// The optional [`RevealedWeb3Name`] revealed in the Merkle proof. - pub web3_name: Option>, - /// The list of linked accounts revealed in the Merkle proof, up to a - /// maximum of `MAX_REVEALED_ACCOUNTS_COUNT`. - pub linked_accounts: BoundedVec>, -} - -impl< - KeyId, - AccountId, - BlockNumber, - Web3Name, - LinkedAccountId, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, - > sp_std::borrow::Borrow<[RevealedDidKey]> - for RevealedDidMerkleProofLeaves< - KeyId, - AccountId, - BlockNumber, - Web3Name, - LinkedAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - > -{ - fn borrow(&self) -> &[RevealedDidKey] { - self.did_keys.borrow() - } -} - -pub enum DidMerkleProofVerifierError { - InvalidMerkleProof, - TooManyRevealedKeys, - TooManyRevealedAccounts, -} - -impl From for u8 { - fn from(value: DidMerkleProofVerifierError) -> Self { - match value { - DidMerkleProofVerifierError::InvalidMerkleProof => 0, - DidMerkleProofVerifierError::TooManyRevealedKeys => 1, - DidMerkleProofVerifierError::TooManyRevealedAccounts => 2, - } - } -} - -/// A function that verifies a DIP Merkle proof revealing some leaves -/// representing parts of a KILT DID identity stored on the KILT chain. -/// If cross-chain DID signatures are not required for the specific use case, -/// this verifier can also be used on its own, without any DID signature -/// verification. -/// The Merkle proof is assumed to have been generated using one of the -/// versioned identity commitment generators, as shown in the [KILT runtime -/// definitions](../../../runtimes/common/src/dip/README.md). -/// The generic types are the following: -/// * `Hasher`: The hasher used by the producer to hash the Merkle leaves and -/// produce the identity commitment. -/// * `KeyId`: The type of a DID key ID according to the producer's definition. -/// * `AccountId`: The type of an account ID according to the producer's -/// definition. -/// * `BlockNumber`: The type of a block number according to the producer's -/// definition. -/// * `Web3Name`: The type of a web3names according to the producer's -/// definition. -/// * `LinkedAccountId`: The type of a DID-linked account ID according to the -/// producer's definition. -/// * `MAX_REVEALED_KEYS_COUNT`: The maximum number of DID keys that are -/// supported when verifying the Merkle proof. -/// * `MAX_REVEALED_ACCOUNTS_COUNT`: The maximum number of linked accounts that -/// are supported when verifying the Merkle proof. -pub(crate) fn verify_dip_merkle_proof< - Hasher, - KeyId, - AccountId, - BlockNumber, - Web3Name, - LinkedAccountId, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, ->( - identity_commitment: &Hasher::Out, - proof: DidMerkleProof< - crate::BoundedBlindedValue, - RevealedDidMerkleProofLeaf, - >, -) -> Result< - RevealedDidMerkleProofLeaves< - KeyId, - AccountId, - BlockNumber, - Web3Name, - LinkedAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - >, - DidMerkleProofVerifierError, -> -where - BlockNumber: Encode + Clone, - Hasher: sp_core::Hasher, - KeyId: Encode + Clone, - AccountId: Encode + Clone, - LinkedAccountId: Encode + Clone, - Web3Name: Encode + Clone, -{ - // TODO: more efficient by removing cloning and/or collecting. - // Did not find another way of mapping a Vec<(Vec, Vec)> to a - // Vec<(Vec, Option>)>. - let proof_leaves = proof - .revealed - .iter() - .map(|leaf| (leaf.encoded_key(), Some(leaf.encoded_value()))) - .collect::, Option>)>>(); - let res = verify_trie_proof::, _, _, _>(identity_commitment, &proof.blinded, &proof_leaves); - cfg_if::cfg_if! { - if #[cfg(feature = "runtime-benchmarks")] { - drop(res); - } else { - res.map_err(|_| DidMerkleProofVerifierError::InvalidMerkleProof)?; - } - } - - // At this point, we know the proof is valid. We just need to map the revealed - // leaves to something the consumer can easily operate on. - #[allow(clippy::type_complexity)] - let (did_keys, web3_name, linked_accounts): ( - BoundedVec, ConstU32>, - Option>, - BoundedVec>, - ) = proof.revealed.into_iter().try_fold( - ( - BoundedVec::with_bounded_capacity(MAX_REVEALED_KEYS_COUNT.saturated_into()), - None, - BoundedVec::with_bounded_capacity(MAX_REVEALED_ACCOUNTS_COUNT.saturated_into()), - ), - |(mut keys, web3_name, mut linked_accounts), leaf| match leaf { - RevealedDidMerkleProofLeaf::DidKey(key_id, key_value) => { - let res = keys.try_push(RevealedDidKey { - id: key_id.0, - relationship: key_id.1, - details: key_value.0, - }); - cfg_if::cfg_if! { - if #[cfg(feature = "runtime-benchmarks")] { - drop(res); - } else { - res.map_err(|_| DidMerkleProofVerifierError::TooManyRevealedKeys)?; - } - } - - Ok::<_, DidMerkleProofVerifierError>((keys, web3_name, linked_accounts)) - } - RevealedDidMerkleProofLeaf::Web3Name(revealed_web3_name, details) => Ok(( - keys, - Some(RevealedWeb3Name { - web3_name: revealed_web3_name.0, - claimed_at: details.0, - }), - linked_accounts, - )), - RevealedDidMerkleProofLeaf::LinkedAccount(account_id, _) => { - let res = linked_accounts.try_push(account_id.0); - cfg_if::cfg_if! { - if #[cfg(feature = "runtime-benchmarks")] { - drop(res); - } else { - res.map_err(|_| DidMerkleProofVerifierError::TooManyRevealedAccounts)?; - } - } - - Ok::<_, DidMerkleProofVerifierError>((keys, web3_name, linked_accounts)) - } - }, - )?; - - Ok(RevealedDidMerkleProofLeaves { - did_keys, - web3_name, - linked_accounts, - }) -} diff --git a/crates/kilt-dip-primitives/src/merkle/mod.rs b/crates/kilt-dip-primitives/src/merkle/mod.rs new file mode 100644 index 000000000..552a922f8 --- /dev/null +++ b/crates/kilt-dip-primitives/src/merkle/mod.rs @@ -0,0 +1,23 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +pub mod v0; + +pub mod latest { + pub use super::v0::*; +} diff --git a/crates/kilt-dip-primitives/src/merkle/v0.rs b/crates/kilt-dip-primitives/src/merkle/v0.rs new file mode 100644 index 000000000..d6124bb76 --- /dev/null +++ b/crates/kilt-dip-primitives/src/merkle/v0.rs @@ -0,0 +1,1303 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2023 BOTLabs GmbH + +// The KILT Blockchain is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The KILT Blockchain is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +//! Module to deal with cross-chain Merkle proof as generated by the KILT chain. +use did::{ + did_details::{DidPublicKey, DidPublicKeyDetails}, + DidSignature, DidVerificationKeyRelationship, +}; +use frame_support::ensure; +use pallet_dip_provider::IdentityCommitmentOf; +use parity_scale_codec::{Codec, Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_core::{ConstU32, U256}; +use sp_runtime::{ + generic::Header, + traits::{AtLeast32BitUnsigned, Hash, Header as HeaderT, MaybeDisplay, Member}, + BoundedVec, SaturatedConversion, +}; +use sp_std::{fmt::Debug, vec::Vec}; +use sp_trie::{verify_trie_proof, LayoutV1}; + +use crate::{ + state_proofs::{verify_storage_value_proof, verify_storage_value_proof_with_decoder, MerkleProofError}, + traits::{BenchmarkDefault, GetWithArg}, + utils::{ + calculate_dip_identity_commitment_storage_key_for_runtime, calculate_parachain_head_storage_key, + BoundedBlindedValue, OutputOf, + }, +}; + +/// The state proof for a parachain head. +/// +/// The generic types indicate the following: +/// * `RelayBlockNumber`: The `BlockNumber` definition of the relaychain. +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo)] +pub struct ProviderHeadStateProof { + pub(crate) relay_block_number: RelayBlockNumber, + pub(crate) proof: BoundedBlindedValue, +} + +#[cfg(feature = "runtime-benchmarks")] +impl kilt_support::traits::GetWorstCase for ProviderHeadStateProof +where + RelayBlockNumber: Default, +{ + fn worst_case(context: Context) -> Self { + Self { + relay_block_number: RelayBlockNumber::default(), + proof: BoundedBlindedValue::worst_case(context), + } + } +} + +/// The state proof for a DIP commitment. +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo)] +pub struct DipCommitmentStateProof(pub(crate) BoundedBlindedValue); + +#[cfg(feature = "runtime-benchmarks")] +impl kilt_support::traits::GetWorstCase for DipCommitmentStateProof { + fn worst_case(context: Context) -> Self { + Self(BoundedBlindedValue::worst_case(context)) + } +} + +/// The Merkle proof for a KILT DID. +/// +/// The generic types indicate the following: +/// * `ProviderDidKeyId`: The DID key ID type configured by the provider. +/// * `ProviderAccountId`: The `AccountId` type configured by the provider. +/// * `ProviderBlockNumber`: The `BlockNumber` type configured by the provider. +/// * `ProviderWeb3Name`: The web3name type configured by the provider. +/// * `ProviderLinkableAccountId`: The linkable account ID type configured by +/// the provider. +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo)] +pub struct DidMerkleProof< + ProviderDidKeyId, + ProviderAccountId, + ProviderBlockNumber, + ProviderWeb3Name, + ProviderLinkableAccountId, +> { + pub(crate) blinded: BoundedBlindedValue, + pub(crate) revealed: Vec< + RevealedDidMerkleProofLeaf< + ProviderDidKeyId, + ProviderAccountId, + ProviderBlockNumber, + ProviderWeb3Name, + ProviderLinkableAccountId, + >, + >, +} + +impl + DidMerkleProof +{ + pub fn new( + blinded: BoundedBlindedValue, + revealed: Vec< + RevealedDidMerkleProofLeaf< + ProviderDidKeyId, + ProviderAccountId, + ProviderBlockNumber, + ProviderWeb3Name, + ProviderLinkableAccountId, + >, + >, + ) -> Self { + Self { blinded, revealed } + } +} + +#[cfg(feature = "runtime-benchmarks")] +impl< + ProviderDidKeyId, + ProviderAccountId, + ProviderBlockNumber, + ProviderWeb3Name, + ProviderLinkableAccountId, + Context, + > kilt_support::traits::GetWorstCase + for DidMerkleProof< + ProviderDidKeyId, + ProviderAccountId, + ProviderBlockNumber, + ProviderWeb3Name, + ProviderLinkableAccountId, + > where + ProviderDidKeyId: Default + Clone, + ProviderAccountId: Clone, + ProviderBlockNumber: Default + Clone, + ProviderWeb3Name: Clone, + ProviderLinkableAccountId: Clone, +{ + fn worst_case(context: Context) -> Self { + Self { + blinded: BoundedBlindedValue::worst_case(context), + revealed: sp_std::vec![RevealedDidMerkleProofLeaf::default(); 64], + } + } +} + +/// A DID signature anchored to a specific block height. +/// +/// The generic types indicate the following: +/// * `BlockNumber`: The `BlockNumber` definition of the chain consuming (i.e., +/// validating) this signature. +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo)] +pub struct TimeBoundDidSignature { + /// The signature. + pub(crate) signature: DidSignature, + /// The block number until the signature is to be considered valid. + pub(crate) valid_until: BlockNumber, +} + +#[cfg(feature = "runtime-benchmarks")] +impl kilt_support::traits::GetWorstCase for TimeBoundDidSignature +where + DidSignature: kilt_support::traits::GetWorstCase, + BlockNumber: Default, +{ + fn worst_case(context: Context) -> Self { + Self { + signature: DidSignature::worst_case(context), + valid_until: BlockNumber::default(), + } + } +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, TypeInfo)] +pub enum Error { + InvalidRelayHeader, + RelayBlockNotFound, + RelayStateRootNotFound, + InvalidDidMerkleProof, + TooManyLeavesRevealed, + InvalidSignatureTime, + InvalidDidKeyRevealed, + ParaHeadMerkleProof(MerkleProofError), + DipCommitmentMerkleProof(MerkleProofError), + Internal, +} + +impl From for u8 { + fn from(value: Error) -> Self { + match value { + // DO NOT USE 0 + // Errors of different sub-parts are separated by a `u8::MAX`. + // A value of 0 would make it confusing whether it's the previous sub-part error (u8::MAX) + // or the new sub-part error (u8::MAX + 0). + Error::InvalidRelayHeader => 1, + Error::RelayBlockNotFound => 2, + Error::RelayStateRootNotFound => 3, + Error::InvalidDidMerkleProof => 4, + Error::TooManyLeavesRevealed => 5, + Error::InvalidSignatureTime => 6, + Error::InvalidDidKeyRevealed => 7, + Error::ParaHeadMerkleProof(error) => match error { + MerkleProofError::InvalidProof => 11, + MerkleProofError::RequiredLeafNotRevealed => 12, + MerkleProofError::ResultDecoding => 13, + }, + Error::DipCommitmentMerkleProof(error) => match error { + MerkleProofError::InvalidProof => 21, + MerkleProofError::RequiredLeafNotRevealed => 22, + MerkleProofError::ResultDecoding => 23, + }, + Error::Internal => u8::MAX, + } + } +} + +/// A DIP proof submitted to a relaychain consumer. +/// +/// The generic types indicate the following: +/// * `RelayBlockNumber`: The `BlockNumber` definition of the relaychain. +/// * `RelayHasher`: The hashing algorithm used by the relaychain. +/// * `KiltDidKeyId`: The DID key ID type configured by the KILT chain. +/// * `KiltAccountId`: The `AccountId` type configured by the KILT chain. +/// * `KiltBlockNumber`: The `BlockNumber` type configured by the KILT chain. +/// * `KiltWeb3Name`: The web3name type configured by the KILT chain. +/// * `KiltLinkableAccountId`: The linkable account ID type configured by the +/// KILT chain. +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo)] +pub struct RelayDipDidProof< + RelayBlockNumber: Copy + Into + TryFrom, + RelayHasher: Hash, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, +> { + /// The relaychain header for the relaychain block specified in the + /// `provider_head_proof`. + pub(crate) relay_header: Header, + /// The state proof for the given parachain head. + pub(crate) provider_head_proof: ProviderHeadStateProof, + /// The raw state proof for the DIP commitment of the given subject. + pub(crate) dip_commitment_proof: DipCommitmentStateProof, + /// The Merkle proof of the subject's DID details. + pub(crate) dip_proof: + DidMerkleProof, + /// The cross-chain DID signature. + pub(crate) signature: TimeBoundDidSignature, +} + +impl< + RelayBlockNumber: Member + sp_std::hash::Hash + Copy + MaybeDisplay + AtLeast32BitUnsigned + Codec + Into + TryFrom, + RelayHasher: Hash, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + > + RelayDipDidProof< + RelayBlockNumber, + RelayHasher, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + > +{ + /// Verifies the relaychain part of the state proof using the provided block + /// hash. + #[allow(clippy::type_complexity)] + pub fn verify_relay_header_with_block_hash( + self, + block_hash: &OutputOf, + ) -> Result< + RelayDipDidProofWithVerifiedRelayStateRoot< + OutputOf, + RelayBlockNumber, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + >, + Error, + > { + if block_hash != &self.relay_header.hash() { + return Err(Error::InvalidRelayHeader); + } + + Ok(RelayDipDidProofWithVerifiedRelayStateRoot { + relay_state_root: self.relay_header.state_root, + provider_head_proof: self.provider_head_proof, + dip_commitment_proof: self.dip_commitment_proof, + dip_proof: self.dip_proof, + signature: self.signature, + }) + } + + /// Verifies the relaychain part of the state proof using the block hash + /// returned by the provided implementation. + /// + /// The generic types indicate the following: + /// * `RelayHashStore`: The type that returns a relaychain block hash given + /// a relaychain block number. + #[allow(clippy::type_complexity)] + pub fn verify_relay_header( + self, + ) -> Result< + RelayDipDidProofWithVerifiedRelayStateRoot< + OutputOf, + RelayBlockNumber, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + >, + Error, + > + where + RelayHashStore: GetWithArg>>, + { + let relay_block_hash = RelayHashStore::get(&self.relay_header.number).ok_or(Error::RelayBlockNotFound)?; + self.verify_relay_header_with_block_hash(&relay_block_hash) + } +} + +/// A DIP proof submitted to a relaychain consumer that has had the proof header +/// verified against a given block hash. +/// +/// The generic types indicate the following: +/// * `StateRoot`: The type of the state root used by the relaychain. +/// * `RelayBlockNumber`: The `BlockNumber` definition of the relaychain. +/// * `KiltDidKeyId`: The DID key ID type configured by the KILT chain. +/// * `KiltAccountId`: The `AccountId` type configured by the KILT chain. +/// * `KiltBlockNumber`: The `BlockNumber` type configured by the KILT chain. +/// * `KiltWeb3Name`: The web3name type configured by the KILT chain. +/// * `KiltLinkableAccountId`: The linkable account ID type configured by the +/// KILT chain. +#[derive(Debug)] +pub struct RelayDipDidProofWithVerifiedRelayStateRoot< + StateRoot, + RelayBlockNumber, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, +> { + /// The verified state root for the relaychain at the block specified in the + /// proof. + pub(crate) relay_state_root: StateRoot, + /// The state proof for the given parachain head. + pub(crate) provider_head_proof: ProviderHeadStateProof, + /// The raw state proof for the DIP commitment of the given subject. + pub(crate) dip_commitment_proof: DipCommitmentStateProof, + /// The Merkle proof of the subject's DID details. + pub(crate) dip_proof: + DidMerkleProof, + /// The cross-chain DID signature. + pub(crate) signature: TimeBoundDidSignature, +} + +impl< + StateRoot, + RelayBlockNumber, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + > + RelayDipDidProofWithVerifiedRelayStateRoot< + StateRoot, + RelayBlockNumber, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + > where + KiltBlockNumber: BenchmarkDefault, +{ + /// Verifies the head data of the state proof for the provider with the + /// given para ID. + /// + /// The generic types indicate the following: + /// * `RelayHasher`: The head data hashing algorithm used by the relaychain. + /// * `ProviderHeader`: The type of the parachain header to be revealed in + /// the state proof. + #[allow(clippy::type_complexity)] + pub fn verify_provider_head_proof( + self, + provider_para_id: u32, + ) -> Result< + DipDidProofWithVerifiedRelayStateRoot< + OutputOf, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + RelayBlockNumber, + >, + Error, + > + where + RelayHasher: Hash, + ProviderHeader: Decode + HeaderT, Number = KiltBlockNumber>, + { + let parachain_dip_proof = ParachainDipDidProof { + provider_head_proof: self.provider_head_proof, + dip_commitment_proof: self.dip_commitment_proof, + dip_proof: self.dip_proof, + signature: self.signature, + }; + + parachain_dip_proof.verify_provider_head_proof_with_state_root::( + provider_para_id, + &self.relay_state_root, + ) + } +} + +/// A DIP proof submitted to a parachain consumer. +/// +/// The generic types indicate the following: +/// * `RelayBlockNumber`: The `BlockNumber` definition of the relaychain. +/// * `KiltDidKeyId`: The DID key ID type configured by the KILT chain. +/// * `KiltAccountId`: The `AccountId` type configured by the KILT chain. +/// * `KiltBlockNumber`: The `BlockNumber` type configured by the KILT chain. +/// * `KiltWeb3Name`: The web3name type configured by the KILT chain. +/// * `KiltLinkableAccountId`: The linkable account ID type configured by the +/// KILT chain. +/// * `ConsumerBlockNumber`: The `BlockNumber` definition of the consumer +/// parachain. +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo)] +pub struct ParachainDipDidProof< + RelayBlockNumber, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, +> { + /// The state proof for the given parachain head. + pub(crate) provider_head_proof: ProviderHeadStateProof, + /// The raw state proof for the DIP commitment of the given subject. + pub(crate) dip_commitment_proof: DipCommitmentStateProof, + /// The Merkle proof of the subject's DID details. + pub(crate) dip_proof: + DidMerkleProof, + /// The cross-chain DID signature. + pub(crate) signature: TimeBoundDidSignature, +} + +#[cfg(feature = "runtime-benchmarks")] +impl< + RelayBlockNumber, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, + Context, + > kilt_support::traits::GetWorstCase + for ParachainDipDidProof< + RelayBlockNumber, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, + > where + RelayBlockNumber: Default, + KiltDidKeyId: Default + Clone, + KiltAccountId: Clone, + KiltBlockNumber: Default + Clone, + KiltWeb3Name: Clone, + KiltLinkableAccountId: Clone, + ConsumerBlockNumber: Default, + Context: Clone, +{ + fn worst_case(context: Context) -> Self { + Self { + provider_head_proof: ProviderHeadStateProof::worst_case(context.clone()), + dip_commitment_proof: DipCommitmentStateProof::worst_case(context.clone()), + dip_proof: DidMerkleProof::worst_case(context.clone()), + signature: TimeBoundDidSignature::worst_case(context), + } + } +} + +impl< + RelayBlockNumber, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, + > + ParachainDipDidProof< + RelayBlockNumber, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, + > where + KiltBlockNumber: BenchmarkDefault, +{ + /// Verifies the head data of the state proof for the provider with the + /// given para ID and relaychain state root. + /// + /// The generic types indicate the following: + /// * `RelayHasher`: The head data hashing algorithm used by the relaychain. + /// * `ProviderHeader`: The type of the parachain header to be revealed in + /// the state proof. + #[allow(clippy::type_complexity)] + pub fn verify_provider_head_proof_with_state_root( + self, + provider_para_id: u32, + relay_state_root: &OutputOf, + ) -> Result< + DipDidProofWithVerifiedRelayStateRoot< + OutputOf, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, + >, + Error, + > + where + RelayHasher: Hash, + ProviderHeader: Decode + HeaderT, Number = KiltBlockNumber>, + { + let provider_head_storage_key = calculate_parachain_head_storage_key(provider_para_id); + // TODO: Figure out why RPC call returns 2 bytes in front which we don't need + let provider_header_result = verify_storage_value_proof_with_decoder::<_, RelayHasher, ProviderHeader>( + &provider_head_storage_key, + *relay_state_root, + self.provider_head_proof.proof, + |input| { + if input.len() < 2 { + return None; + } + let mut trimmed_input = &input[2..]; + ProviderHeader::decode(&mut trimmed_input).ok() + }, + ); + cfg_if::cfg_if! { + if #[cfg(feature = "runtime-benchmarks")] { + let provider_header = provider_header_result.unwrap_or_else(|_| ProviderHeader::new(::Number::default(), ::Hash::default(), ::Hash::default(), ::Hash::default(), sp_runtime::Digest::default())); + } else { + let provider_header = provider_header_result.map_err(Error::ParaHeadMerkleProof)?; + } + } + Ok(DipDidProofWithVerifiedRelayStateRoot { + state_root: *provider_header.state_root(), + dip_commitment_proof: self.dip_commitment_proof, + dip_proof: self.dip_proof, + signature: self.signature, + }) + } + + /// Verifies the head data of the state proof for the provider with the + /// given para ID using the state root returned by the provided + /// implementation. + /// + /// The generic types indicate the following: + /// * `RelayHasher`: The hashing algorithm used on the relaychain to + /// generate the parachains head data. + /// * `StateRootStore`: The type that returns a relaychain state root given + /// a relaychain block number. + /// * `ProviderHeader`: The type of the parachain header to be revealed in + /// the state proof. + #[allow(clippy::type_complexity)] + pub fn verify_provider_head_proof( + self, + provider_para_id: u32, + ) -> Result< + DipDidProofWithVerifiedRelayStateRoot< + OutputOf, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, + >, + Error, + > + where + RelayHasher: Hash, + StateRootStore: GetWithArg>>, + ProviderHeader: Decode + HeaderT, Number = KiltBlockNumber>, + { + let relay_state_root = StateRootStore::get(&self.provider_head_proof.relay_block_number); + cfg_if::cfg_if! { + if #[cfg(feature = "runtime-benchmarks")] { + let relay_state_root = relay_state_root.unwrap_or_default(); + } else { + let relay_state_root = relay_state_root.ok_or(Error::RelayStateRootNotFound)?; + } + } + self.verify_provider_head_proof_with_state_root::( + provider_para_id, + &relay_state_root, + ) + } +} + +/// A DIP proof that has had the proof header and the relaychain state verified +/// for the provided relaychain block number. +/// +/// The generic types indicate the following: +/// * `StateRoot`: The type of the relaychain state root. +/// * `KiltDidKeyId`: The DID key ID type configured by the KILT chain. +/// * `KiltAccountId`: The `AccountId` type configured by the KILT chain. +/// * `KiltBlockNumber`: The `BlockNumber` type configured by the KILT chain. +/// * `KiltWeb3Name`: The web3name type configured by the KILT chain. +/// * `KiltLinkableAccountId`: The linkable account ID type configured by the +/// KILT chain. +/// * `ConsumerBlockNumber`: The `BlockNumber` definition of the consumer +/// parachain. +#[derive(Debug)] +pub struct DipDidProofWithVerifiedRelayStateRoot< + StateRoot, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, +> { + /// The relaychain state root for the block specified in the DIP proof. + pub(crate) state_root: StateRoot, + /// The raw state proof for the DIP commitment of the given subject. + pub(crate) dip_commitment_proof: DipCommitmentStateProof, + /// The Merkle proof of the subject's DID details. + pub(crate) dip_proof: + DidMerkleProof, + /// The cross-chain DID signature. + pub(crate) signature: TimeBoundDidSignature, +} + +impl< + StateRoot, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, + > + DipDidProofWithVerifiedRelayStateRoot< + StateRoot, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, + > +{ + /// Verifies the DIP commitment part of the state proof for the subject with + /// the given identifier. + /// + /// The generic types indicate the following: + /// * `ParachainHasher`: The hashing algorithm used to hash storage on the + /// parachain. + /// * `ProviderRuntime`: The provider runtime definition. + #[allow(clippy::type_complexity)] + pub fn verify_dip_commitment_proof_for_subject( + self, + subject: &ProviderRuntime::Identifier, + ) -> Result< + DipDidProofWithVerifiedSubjectCommitment< + IdentityCommitmentOf, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, + >, + Error, + > + where + StateRoot: Ord, + ParachainHasher: Hash, + ProviderRuntime: pallet_dip_provider::Config, + IdentityCommitmentOf: BenchmarkDefault, + { + let dip_commitment_storage_key = + calculate_dip_identity_commitment_storage_key_for_runtime::(subject, 0); + let dip_commitment_result = + verify_storage_value_proof::<_, ParachainHasher, IdentityCommitmentOf>( + &dip_commitment_storage_key, + self.state_root, + self.dip_commitment_proof.0, + ); + cfg_if::cfg_if! { + if #[cfg(feature = "runtime-benchmarks")] { + let dip_commitment = dip_commitment_result.unwrap_or_default(); + } else { + let dip_commitment = dip_commitment_result.map_err(Error::DipCommitmentMerkleProof)?; + } + } + Ok(DipDidProofWithVerifiedSubjectCommitment { + dip_commitment, + dip_proof: self.dip_proof, + signature: self.signature, + }) + } +} + +/// A DIP proof that has had the relaychain state and the DIP commitment +/// verified for the provided relaychain block number. +/// +/// The generic types indicate the following: +/// * `Commitment`: The DIP identity commitment type configured by the KILT +/// chain. +/// * `KiltDidKeyId`: The DID key ID type configured by the KILT chain. +/// * `KiltAccountId`: The `AccountId` type configured by the KILT chain. +/// * `KiltBlockNumber`: The `BlockNumber` type configured by the KILT chain. +/// * `KiltWeb3Name`: The web3name type configured by the KILT chain. +/// * `KiltLinkableAccountId`: The linkable account ID type configured by the +/// KILT chain. +/// * `ConsumerBlockNumber`: The `BlockNumber` definition of the consumer +/// parachain. +#[derive(Debug)] +pub struct DipDidProofWithVerifiedSubjectCommitment< + Commitment, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, +> { + /// The verified DIP identity commitment. + pub(crate) dip_commitment: Commitment, + /// The Merkle proof of the subject's DID details. + pub(crate) dip_proof: + DidMerkleProof, + /// The cross-chain DID signature. + pub(crate) signature: TimeBoundDidSignature, +} + +impl< + Commitment, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, + > + DipDidProofWithVerifiedSubjectCommitment< + Commitment, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, + > where + KiltDidKeyId: Encode, + KiltAccountId: Encode, + KiltBlockNumber: Encode, + KiltWeb3Name: Encode, + KiltLinkableAccountId: Encode, +{ + /// Verifies the Merkle proof of the subject's DID details. + /// + /// The generic types indicate the following: + /// * `DidMerkleHasher`: The hashing algorithm used to merkleize the DID + /// details. + /// * `MAX_REVEALED_LEAVES_COUNT`: The maximum number of leaves revealable + /// in the proof. + pub fn verify_dip_proof( + self, + ) -> Result< + DipRevealedDetailsAndUnverifiedDidSignature< + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, + MAX_REVEALED_LEAVES_COUNT, + >, + Error, + > + where + DidMerkleHasher: Hash, + { + ensure!( + self.dip_proof.revealed.len() <= MAX_REVEALED_LEAVES_COUNT.saturated_into(), + Error::TooManyLeavesRevealed + ); + + let proof_leaves_key_value_pairs = self + .dip_proof + .revealed + .iter() + .map(|revealed_leaf| (revealed_leaf.encoded_key(), Some(revealed_leaf.encoded_value()))) + .collect::>(); + let proof_verification_result = verify_trie_proof::, _, _, _>( + &self.dip_commitment, + self.dip_proof.blinded.as_slice(), + proof_leaves_key_value_pairs.as_slice(), + ); + + cfg_if::cfg_if! { + if #[cfg(feature = "runtime-benchmarks")] { + drop(proof_verification_result); + } else { + proof_verification_result.map_err(|_| Error::InvalidDidMerkleProof)?; + } + } + let revealed_leaves = BoundedVec::try_from(self.dip_proof.revealed).map_err(|_| { + log::error!("Should not fail to construct BoundedVec since bounds were checked before."); + Error::Internal + })?; + + Ok(DipRevealedDetailsAndUnverifiedDidSignature { + revealed_leaves, + signature: self.signature, + }) + } +} + +/// A DIP proof whose information has been verified but that contains a +/// cross-chain [`TimeBoundDidSignature`] that still needs verification. +/// +/// The generic types indicate the following: +/// * `KiltDidKeyId`: The DID key ID type configured by the KILT chain. +/// * `KiltAccountId`: The `AccountId` type configured by the KILT chain. +/// * `KiltBlockNumber`: The `BlockNumber` type configured by the KILT chain. +/// * `KiltWeb3Name`: The web3name type configured by the KILT chain. +/// * `KiltLinkableAccountId`: The linkable account ID type configured by the +/// KILT chain. +/// * `ConsumerBlockNumber`: The `BlockNumber` definition of the consumer +/// parachain. +/// * `MAX_REVEALED_LEAVES_COUNT`: The maximum number of leaves revealable in +/// the proof. +#[derive(Debug)] +pub struct DipRevealedDetailsAndUnverifiedDidSignature< + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, + const MAX_REVEALED_LEAVES_COUNT: u32, +> { + /// The parts of the subject's DID details revealed in the DIP proof. + pub(crate) revealed_leaves: BoundedVec< + RevealedDidMerkleProofLeaf, + ConstU32, + >, + /// The cross-chain DID signature. + pub(crate) signature: TimeBoundDidSignature, +} + +impl< + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, + const MAX_REVEALED_LEAVES_COUNT: u32, + > + DipRevealedDetailsAndUnverifiedDidSignature< + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, + MAX_REVEALED_LEAVES_COUNT, + > where + ConsumerBlockNumber: PartialOrd, +{ + /// Verifies that the DIP proof signature is anchored to a block that has + /// not passed on the consumer chain. + pub fn verify_signature_time( + self, + block_number: &ConsumerBlockNumber, + ) -> Result< + DipRevealedDetailsAndVerifiedDidSignatureFreshness< + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + MAX_REVEALED_LEAVES_COUNT, + >, + Error, + > { + cfg_if::cfg_if! { + if #[cfg(feature = "runtime-benchmarks")] { + let _ = self.signature.valid_until >= *block_number; + } else { + frame_support::ensure!(self.signature.valid_until >= *block_number, Error::InvalidSignatureTime); + } + } + Ok(DipRevealedDetailsAndVerifiedDidSignatureFreshness { + revealed_leaves: self.revealed_leaves, + signature: self.signature.signature, + }) + } +} + +/// A DIP proof whose information has been verified and whose signature has been +/// verified not to be expired, but that yet does not contain information as to +/// which of the revealed keys has generated the signature. +/// +/// The generic types indicate the following: +/// * `KiltDidKeyId`: The DID key ID type configured by the KILT chain. +/// * `KiltAccountId`: The `AccountId` type configured by the KILT chain. +/// * `KiltBlockNumber`: The `BlockNumber` type configured by the KILT chain. +/// * `KiltWeb3Name`: The web3name type configured by the KILT chain. +/// * `KiltLinkableAccountId`: The linkable account ID type configured by the +/// KILT chain. +/// * `MAX_REVEALED_LEAVES_COUNT`: The maximum number of leaves revealable in +/// the proof. +#[derive(Debug)] +pub struct DipRevealedDetailsAndVerifiedDidSignatureFreshness< + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + const MAX_REVEALED_LEAVES_COUNT: u32, +> { + /// The parts of the subject's DID details revealed in the DIP proof. + pub(crate) revealed_leaves: BoundedVec< + RevealedDidMerkleProofLeaf, + ConstU32, + >, + /// The cross-chain DID signature without time information. + pub(crate) signature: DidSignature, +} + +impl< + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + const MAX_REVEALED_LEAVES_COUNT: u32, + > + DipRevealedDetailsAndVerifiedDidSignatureFreshness< + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + MAX_REVEALED_LEAVES_COUNT, + > where + KiltDidKeyId: BenchmarkDefault, + KiltBlockNumber: BenchmarkDefault, +{ + /// Iterates over the revealed DID leafs to find the one that generated a + /// valid signature for the provided payload. + pub fn retrieve_signing_leaf_for_payload( + self, + payload: &[u8], + ) -> Result< + DipOriginInfo< + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + MAX_REVEALED_LEAVES_COUNT, + >, + Error, + > { + let revealed_verification_keys = self.revealed_leaves.iter().filter(|leaf| { + matches!( + leaf, + RevealedDidMerkleProofLeaf::DidKey(RevealedDidKey { + relationship: DidKeyRelationship::Verification(_), + .. + }) + ) + }); + let maybe_signing_key_index = revealed_verification_keys + .enumerate() + .find(|(_, revealed_verification_key)| { + let RevealedDidMerkleProofLeaf::DidKey(RevealedDidKey { + details: + DidPublicKeyDetails { + key: DidPublicKey::PublicVerificationKey(verification_key), + .. + }, + .. + }) = revealed_verification_key + else { + return false; + }; + verification_key.verify_signature(payload, &self.signature).is_ok() + }) + .map(|(index, _)| u32::saturated_from(index)); + + let signing_key_entry = if let Some(index) = maybe_signing_key_index { + (self.revealed_leaves, index) + } else { + cfg_if::cfg_if! { + if #[cfg(feature = "runtime-benchmarks")] { + return Ok(DipOriginInfo::default()); + } else { + return Err(Error::InvalidDidKeyRevealed); + } + } + }; + + Ok(DipOriginInfo { + revealed_leaves: signing_key_entry.0, + signing_leaf_index: signing_key_entry.1, + }) + } +} + +/// Information, available as an origin, after the whole DIP proof has been +/// verified. +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub struct DipOriginInfo< + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + const MAX_REVEALED_LEAVES_COUNT: u32, +> { + /// The parts of the subject's DID details revealed in the DIP proof. + revealed_leaves: BoundedVec< + RevealedDidMerkleProofLeaf, + ConstU32, + >, + /// The index of the signing leaf from the vector above, + signing_leaf_index: u32, +} + +impl< + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + const MAX_REVEALED_LEAVES_COUNT: u32, + > + DipOriginInfo< + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + MAX_REVEALED_LEAVES_COUNT, + > +{ + /// Returns an iterator over the revealed DID leaves. + pub fn iter_leaves( + &self, + ) -> impl Iterator< + Item = &RevealedDidMerkleProofLeaf< + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + >, + > { + self.revealed_leaves.iter() + } + + /// Returns a reference to the leaf that signed the cross-chain operation. + /// This operation should never fail, so the only error it returns is an + /// `Error::Internal` which, anyway, should never happen. + pub fn get_signing_leaf(&self) -> Result<&RevealedDidKey, Error> { + let leaf = &self + .revealed_leaves + .get(usize::saturated_from(self.signing_leaf_index)) + .ok_or_else(|| { + log::error!("Should never fail to retrieve the signing leaf."); + Error::Internal + })?; + let RevealedDidMerkleProofLeaf::DidKey(did_key) = leaf else { + log::error!("Should never fail to convert the signing leaf to a DID Key leaf."); + return Err(Error::Internal); + }; + Ok(did_key) + } +} + +#[cfg(feature = "runtime-benchmarks")] +impl< + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + const MAX_REVEALED_LEAVES_COUNT: u32, + > Default + for DipOriginInfo< + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + MAX_REVEALED_LEAVES_COUNT, + > where + KiltDidKeyId: BenchmarkDefault, + KiltBlockNumber: BenchmarkDefault, +{ + fn default() -> Self { + let default_keys = sp_std::vec![RevealedDidKey { + id: KiltDidKeyId::default(), + details: DidPublicKeyDetails { + key: did::did_details::DidVerificationKey::Ed25519(sp_core::ed25519::Public::from_raw([0u8; 32])) + .into(), + block_number: KiltBlockNumber::default() + }, + relationship: DidVerificationKeyRelationship::Authentication.into() + } + .into()]; + let bounded_keys = default_keys + .try_into() + // To avoid requiring types to implement `Debug`. + .map_err(|_| "Should not fail to convert single element to a BoundedVec.") + .unwrap(); + Self { + revealed_leaves: bounded_keys, + signing_leaf_index: 0u32, + } + } +} + +/// Relationship of a key to a DID Document. +#[derive(Clone, Copy, Debug, Encode, Decode, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub enum DidKeyRelationship { + Encryption, + Verification(DidVerificationKeyRelationship), +} + +impl From for DidKeyRelationship { + fn from(value: DidVerificationKeyRelationship) -> Self { + Self::Verification(value) + } +} + +impl TryFrom for DidVerificationKeyRelationship { + type Error = (); + + fn try_from(value: DidKeyRelationship) -> Result { + if let DidKeyRelationship::Verification(rel) = value { + Ok(rel) + } else { + Err(()) + } + } +} + +/// All possible Merkle leaf types that can be revealed as part of a DIP +/// identity Merkle proof. +#[derive(Clone, Copy, Debug, Encode, Decode, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub enum RevealedDidMerkleProofLeaf { + DidKey(RevealedDidKey), + Web3Name(RevealedWeb3Name), + LinkedAccount(RevealedAccountId), +} + +impl From> + for RevealedDidMerkleProofLeaf +{ + fn from(value: RevealedDidKey) -> Self { + Self::DidKey(value) + } +} + +impl From> + for RevealedDidMerkleProofLeaf +{ + fn from(value: RevealedWeb3Name) -> Self { + Self::Web3Name(value) + } +} + +impl From> + for RevealedDidMerkleProofLeaf +{ + fn from(value: RevealedAccountId) -> Self { + Self::LinkedAccount(value) + } +} + +#[cfg(feature = "runtime-benchmarks")] +impl Default + for RevealedDidMerkleProofLeaf +where + KeyId: Default, + BlockNumber: Default, +{ + fn default() -> Self { + RevealedDidKey { + id: KeyId::default(), + relationship: DidVerificationKeyRelationship::Authentication.into(), + details: DidPublicKeyDetails { + key: did::did_details::DidVerificationKey::Ed25519(sp_core::ed25519::Public::from_raw([0u8; 32])) + .into(), + block_number: BlockNumber::default(), + }, + } + .into() + } +} + +impl + RevealedDidMerkleProofLeaf +where + KeyId: Encode, + Web3Name: Encode, + LinkedAccountId: Encode, +{ + pub fn encoded_key(&self) -> Vec { + match self { + RevealedDidMerkleProofLeaf::DidKey(RevealedDidKey { id, relationship, .. }) => (id, relationship).encode(), + RevealedDidMerkleProofLeaf::Web3Name(RevealedWeb3Name { web3_name, .. }) => web3_name.encode(), + RevealedDidMerkleProofLeaf::LinkedAccount(RevealedAccountId(account_id)) => account_id.encode(), + } + } +} + +impl + RevealedDidMerkleProofLeaf +where + AccountId: Encode, + BlockNumber: Encode, +{ + pub fn encoded_value(&self) -> Vec { + match self { + RevealedDidMerkleProofLeaf::DidKey(RevealedDidKey { details, .. }) => details.encode(), + RevealedDidMerkleProofLeaf::Web3Name(RevealedWeb3Name { claimed_at, .. }) => claimed_at.encode(), + RevealedDidMerkleProofLeaf::LinkedAccount(_) => ().encode(), + } + } +} + +/// The details of a DID key after it has been successfully verified in a Merkle +/// proof. +#[derive(Clone, Copy, Debug, Encode, Decode, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub struct RevealedDidKey { + /// The key ID, according to the provider's definition. + pub id: KeyId, + /// The key relationship to the subject's DID Document. + pub relationship: DidKeyRelationship, + /// The details of the DID Key, including its creation block number on the + /// provider chain. + pub details: DidPublicKeyDetails, +} + +/// The details of a web3name after it has been successfully verified in a +/// Merkle proof. +#[derive(Clone, Copy, Debug, Encode, Decode, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub struct RevealedWeb3Name { + /// The web3name. + pub web3_name: Web3Name, + /// The block number on the provider chain in which it was linked to the DID + /// subject. + pub claimed_at: BlockNumber, +} + +/// The details of an account after it has been successfully verified in a +/// Merkle proof. +#[derive(Clone, Copy, Debug, Encode, Decode, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub struct RevealedAccountId(pub AccountId); diff --git a/crates/kilt-dip-primitives/src/state_proofs/mod.rs b/crates/kilt-dip-primitives/src/state_proofs/mod.rs index 36025dbec..2f8eb4c53 100644 --- a/crates/kilt-dip-primitives/src/state_proofs/mod.rs +++ b/crates/kilt-dip-primitives/src/state_proofs/mod.rs @@ -16,66 +16,177 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -//! Module to deal with cross-chain state proofs. +use parity_scale_codec::Decode; +use scale_info::TypeInfo; +use sp_runtime::traits::Hash; +use sp_std::vec::Vec; +use sp_trie::StorageProof; -/// Parachain-related state proof logic. -pub(crate) mod parachain; -/// Relaychain-related state proof logic. -pub(crate) mod relaychain; +use crate::{state_proofs::substrate_no_std_port::read_proof_check, utils::OutputOf}; -// Ported from /~https://github.com/paritytech/substrate/blob/b27c470eaff379f512d1dec052aff5d551ed3b03/primitives/state-machine/src/lib.rs#L1076 +// Ported and adapted from . +// Refer to the original source file for full license details. // Needs to be replaced with its runtime-friendly version when available, or be // kept up-to-date with upstream. -mod substrate_no_std_port { - use hash_db::EMPTY_PREFIX; - use parity_scale_codec::Codec; - use sp_core::Hasher; - use sp_state_machine::{Backend, TrieBackend, TrieBackendBuilder}; - use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; - use sp_trie::{HashDBT, MemoryDB, StorageProof}; - - pub(super) fn read_proof_check( - root: H::Out, - proof: StorageProof, - keys: I, - ) -> Result, Option>>, ()> - where - H: Hasher, - H::Out: Ord + Codec, - I: IntoIterator, - I::Item: AsRef<[u8]>, - { - let proving_backend = create_proof_check_backend::(root, proof)?; - let mut result = BTreeMap::new(); - for key in keys.into_iter() { - let value = read_proof_check_on_proving_backend(&proving_backend, key.as_ref())?; - result.insert(key.as_ref().to_vec(), value); +mod substrate_no_std_port; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, TypeInfo)] +pub enum MerkleProofError { + InvalidProof, + RequiredLeafNotRevealed, + ResultDecoding, +} + +impl From for u8 { + fn from(value: MerkleProofError) -> Self { + match value { + // DO NOT USE 0 + // Errors of different sub-parts are separated by a `u8::MAX`. + // A value of 0 would make it confusing whether it's the previous sub-part error (u8::MAX) + // or the new sub-part error (u8::MAX + 0). + MerkleProofError::InvalidProof => 1, + MerkleProofError::RequiredLeafNotRevealed => 2, + MerkleProofError::ResultDecoding => 3, } - Ok(result) } +} + +/// Verify a Merkle-based storage proof for a given storage key according to the +/// provided state root. The generic types indicate the following: +/// * `StorageKey`: defines the type of the storage key included in the proof. +/// * `MerkleHasher`: defines the hashing algorithm used to calculate the Merkle +/// root. +/// * `Success`: defines the type expected to be revealed in a valid Merkle +/// proof. +pub fn verify_storage_value_proof( + storage_key: &StorageKey, + state_root: OutputOf, + state_proof: impl IntoIterator>, +) -> Result +where + StorageKey: AsRef<[u8]>, + MerkleHasher: Hash, + OutputOf: Ord, + Success: Decode, +{ + verify_storage_value_proof_with_decoder::<_, MerkleHasher, _>(storage_key, state_root, state_proof, |input| { + Success::decode(input).ok() + }) +} + +/// Verify a Merkle-based storage proof for a given storage key according to the +/// provided state root. The generic types indicate the following: +/// * `StorageKey`: defines the type of the storage key included in the proof. +/// * `MerkleHasher`: defines the hashing algorithm used to calculate the Merkle +/// root. +/// * `TransformResult`: the type returned by the provided decoding function, if +/// successful. The `None` result is interpreted as an error, so it is not possible to return a type for which `None` would be a correct decoding, for now. See for more details. +pub fn verify_storage_value_proof_with_decoder( + storage_key: &StorageKey, + state_root: OutputOf, + state_proof: impl IntoIterator>, + // TODO: Switch to `Error` from `Option` for the closure. + // `Error` is not yet available in core. + // It was merged recently and will be stabilized at some point. See /~https://github.com/rust-lang/rust/issues/103765 for more. + mut transform: impl FnMut(&mut &[u8]) -> Option, +) -> Result +where + StorageKey: AsRef<[u8]>, + MerkleHasher: Hash, + OutputOf: Ord, +{ + let storage_proof = StorageProof::new(state_proof); + let mut revealed_leaves = read_proof_check::(state_root, storage_proof, [storage_key].iter()) + .map_err(|_| MerkleProofError::InvalidProof)?; - fn read_proof_check_on_proving_backend( - proving_backend: &TrieBackend, H>, - key: &[u8], - ) -> Result>, ()> - where - H: Hasher, - H::Out: Ord + Codec, - { - proving_backend.storage(key).map_err(|_| ()) + debug_assert!( + revealed_leaves.len() == 1usize, + "Only a single leaf is expected to be revealed in the storage proof." + ); + debug_assert!( + revealed_leaves.contains_key(storage_key.as_ref()), + "Proof does not include the expected storage key." + ); + + let Some(Some(encoded_revealed_leaf)) = revealed_leaves.get_mut(storage_key.as_ref()) else { + return Err(MerkleProofError::RequiredLeafNotRevealed); + }; + + let input = &mut &encoded_revealed_leaf[..]; + transform(input).ok_or(MerkleProofError::ResultDecoding) +} + +#[cfg(test)] +mod test { + use cumulus_primitives_core::relay_chain::HeadData; + use hex_literal::hex; + use parity_scale_codec::Encode; + use sp_core::{storage::StorageKey, H256}; + use sp_runtime::traits::BlakeTwo256; + + use crate::state_proofs::verify_storage_value_proof; + + #[test] + fn spiritnet_system_event_count() { + // As of RPC state_getReadProof(" + // 0x26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850", + // "0x2c0746e7e9ccc6e4d27bcb4118cb6821ae53ae9bf372f4f49ac28d8598f9bed5") + let events_count_storage_key = + StorageKey(frame_support::storage::storage_prefix(b"System", b"EventCount").to_vec()); + let spiritnet_event_count_proof_at_block = [ + hex!("800404645ea5c1b19ab7a04f536c519aca4983ac205cda3f0000000000545e98fdbe9ce6c55837576c60c7af38501005000000").to_vec(), + hex!("80401080481e2bd8085a02c5b58987bce7a69f0b5c7fa651e8e82c5481c94707860be9078067785103d453293707ba847e21df7e35a7a57b8fb929d40465328b6642669fcc").to_vec(), + hex!("80ffff8010623b5a3a9dbc752963d827be0bb855bf3e24258ae09341d5f762e96a836ac180c34b753605e821528756b55b4ddafb742df6e54fbc03ef401d4ebfd6dd4f3e44806f83646e0bf3ca0ac9f2092dea5b0e3caf210cc6b54c3b44a51855a133367a6580b02cde7b1fd3f8d13f698ef6e9daa29b32258d4d97a8947051070a4540aecacd80903d521961849d07ceee132617b8dde96c3ff472f5a9a089d4055ffe7ffd1e988016c29c943c106713bb8f16b776eb7daed005540165696da286cddf6b25d085448019a464010cb746b0589891f72b0eed603d4712b04af46f7bcae724564194801480a305ffe069db7eb21841f75b5939943f62c4abb3a051d530839c5dd935ccbc8a8035d8938b0c856878de1e3fe45a559588b2da52ccf195ab1e3d0aca6ac7bb079d8064019a474a283c19f46ff4652a5e1f636efd4013d3b8a91c49573045c6ff01c0801a191dcb736faddb84889a13c7aa717d260e9b635b30a9eb3907f925a2253d6880f8bc389fc62ca951609bae208b7506bae497623e647424062d1c56cb1f2d2e1c80211a9fb5f8b794f9fbfbdcd4519aa475ecaf9737b4ee513dde275d5fbbe64da080c267d0ead99634e9b9cfbf61a583877e0241ac518e62e909fbb017469de275f780b3059a7226d4b320c25e9b2f8ffe19cf93467e3b306885962c5f34b5671d15fe8092dfba9e30e1bbefab13c792755d06927e6141f7220b7485e5aa40de92401a66").to_vec(), + hex!("9eaa394eea5630e07c48ae0c9558cef7398f8069ef420a0deb5a428c9a08563b28a78874bba09124eecc8d28bf30b0e2ddd310745f04abf5cb34d6244378cddbf18e849d962c000000000736d8e8140100505f0e7b9012096b41c4eb3aaf947f6ea4290800004c5f0684a022a34dd8bfa2baaf44f172b710040180dd3270a03a1a13fc20bcdf24d1aa4ddccc6183db2e2e153b8a68ba8540699a8a80b413dad63538a591f7f2575d287520ee44d7143aa5ec2411969861e1f55a2989804c3f0f541a13980689894db7c60c785dd29e066f213bb29b17aa740682ad7efd8026d3a50544f5c89500745aca2be36cfe076f599c5115192fb9deae227e2710c980bd04b00bf6b42756a06a4fbf05a5231c2094e48182eca95d2cff73ab907592aa").to_vec(), + ].to_vec(); + let spiritnet_state_root: H256 = + hex!("94c23fda279cea4a4370e90f1544c8938923dfd4ac201a420c7a26fb0d3caf8c").into(); + // As of query system::eventCount() at block + // "0x2c0746e7e9ccc6e4d27bcb4118cb6821ae53ae9bf372f4f49ac28d8598f9bed5" which + // results in the key + // "0x26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850" + let expected_event_count_at_block = 5u32; + let event_count_at_block = verify_storage_value_proof::<_, BlakeTwo256, u32>( + &events_count_storage_key, + spiritnet_state_root, + spiritnet_event_count_proof_at_block, + ) + .expect("Merkle proof verification for pre-calculated Spiritnet storage entry should not fail."); + assert_eq!(expected_event_count_at_block, event_count_at_block); } - fn create_proof_check_backend(root: H::Out, proof: StorageProof) -> Result, H>, ()> - where - H: Hasher, - H::Out: Codec, - { - let db = proof.into_memory_db(); - - if db.contains(&root, EMPTY_PREFIX) { - Ok(TrieBackendBuilder::new(db, root).build()) - } else { - Err(()) - } + #[test] + fn polkadot_parahead_proof_for_spiritnet() { + // As of RPC state_getReadProof("0xcd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c32c0cfd6c23b92a7826080000", "0x18e90e9aa8e3b063f60386ba1b0415111798e72d01de58b1438d620d42f58e39") + let spiritnet_head_storage_key = StorageKey( + [ + frame_support::storage::storage_prefix(b"Paras", b"Heads").as_slice(), + sp_io::hashing::twox_64(2_086u32.encode().as_ref()).as_slice(), + 2_086u32.encode().as_slice(), + ] + .concat(), + ); + let spiritnet_head_proof_at_block = [ + hex!("570c0cfd6c23b92a7826080000f102e90265541097fb02782e14f43074f0b00e44ae8e9fe426982323ef1d329739740d37f252ff006d1156941db1bccd58ce3a1cac4f40cad91f692d94e98f501dd70081a129b69a3e2ef7e1ff84ba3d86dab4e95f2c87f6b1055ebd48519c185360eae58f05d1ea08066175726120dcdc6308000000000561757261010170ccfaf3756d1a8dd8ae5c89094199d6d32e5dd9f0920f6fe30f986815b5e701974ea0e0e0a901401f2c72e3dd8dbdf4aa55d59bf3e7021856cdb8038419eb8c").to_vec(), + hex!("80046480186b1513c5112466ada33da3c65558979906ca9fb82510b62f6ea01f550a4807808bc90ded5636f31c8395a315b5f27a1a25a2ceebd36921a518669ce7e52f80e680993c5e952e6e4f72f295ba04951ace9029b23e9a87887b41895c16f77bec42ee80b798b224c5ee3d668519e75ca98504116f645fb969a5e2653a298b0181f9a694").to_vec(), + hex!("80ffff806ecd86e87715a007ee9b216d8a99a604773014260d51f6552b6fbd7c21786d9c80e23ef51809d6c80c01a6e264ff0d298cce01c1addfdbb0789597b9a6b3f3e4fd80c9c5f0f29d777e2cebcdbd06ddf1c2cfa8ee83524b37ace99d8b7a3aeff039b380da013185503cfefa6c9cc88751993f1f2bf4b8fa4918e876f499fb9405e3206c803a89668f636552a0fb93619913dcc46cf3e087363d532b76a345155a44a46b5180c2e7fc654720b7dcc0316ae1591fde4beb8b853a343b7e5e3ee564d2692c2ee280840f9c4ae7c16ae948828bf50faf062264402e6134d2d6144a5e3ecb0a1e1d9c80f93c2be1ef51fb2032445cc7fbc2023b9e3b8cf8c0d832b464ae48a020bfaa8c8010c63537c9bf58d50c8c0e13c154fd88b2f683e13701901bdc64565aa9b756d580f0b60eaf17fb680827e8a8938c717ac943b85ff373c0fc911e06c34a3a30327280ccb29f1efa59fd7c80a730cb88789a5a256b01fee7e83ac9a3c90da330adc7a480c8e57c547edb33d4b712f017f09d2de2e055f18815669c83eef2f7f3e5dcafae80b7b7e7ffc91a7dd4c4902f7f15cd7598d1258a75433ea953565661d114e2dcca80ebc3a2df819c7c2fd1a33eb1d484beaf7b71114d6a6db240d8b07dc10bfdc49b80a71f21aa3fa5d7475bf134d50f25e2515c797d0a4c2e93998888150c1f969ab8801e32613f54e70c95e9b16a14f5797522ef5e2ef7867868ff663e33e8880994ed").to_vec(), + hex!("9e710b30bd2eab0352ddcc26417aa1945fd380d49ebc7ca5c1b751c2badb5e5a326d3ba9e331d8b7c6cf279ed7fd71a8882b6c8038088652f73dc8a22336d10f492f0ef8836beaba0ccfeb0f8fabdc9df1d17e2d807f88402cbbed7fa3307e07044200b572d5e8e12913b41e1923dcb2c0799bc2be804d57e9a8e4934fab698a9db50682052ee9459c666a075d1bfc471da8e5da14da80b9aee043e378f8313e68a6030679ccf3880fa1e7ab19b6244b5c262b7a152f004c5f03c716fb8fff3de61a883bb76adb34a2040080f282bc12648ffb197ffc257edc7ff3a3fdda452daa51091ccbd2dfb91d8aa9518008a0c609ab4888f02c2545c002153297c2641c5a7b4f3d8e25c634e721f80bea80b6617c764df278313c426c46961ccde8ee7a03f9007b74bc8bc6c49d1583cf7d8077b493d45eb153353026cc330307e0753ac41a5cb8e843ceb1efdc46655f33a0808bdaa43fc5dc0e928e2da0ce8ed02096b0b74c61feaba2546980ed9c6174f71d").to_vec(), + hex!("9f0b3c252fcb29d88eff4f3de5de4476c3ffbf8013c601cc93de3437f9d415bd52c48d794b341f218b9d0020a4b646746c24d0ca80348b8e2c39c479a146933297f62b7051df82e92e1bca761432c3e6f64c74033f80220131e7cd7a08b97f8aa06225f7aefbbca8118fb436c07689c552ed3f577145806d974dd9e4db5e407e29f84c4121ccc58f9c6adc3933afc1bcaef52defe77de5801e9e1a21db053de56365fdee57998488ddae7d664c0430da90469dde17936c1f80c5c11751bbfc99a1ad805c58a65b9704e0bad58e694023e9cc57ce6ef84cdb0b8038f6c242700eaea04ffad5c25ca9a9b1cc2af7303655a32eb59e84b6bb927cd3802575469e76e104b0db8b18dbc762b997a78aa666432a44c4b955ced044a4691f80a81408b856272feeec08845af515e27d033efd3ff8b46de6bc706c38e600086a809ee78332c2a38a3918070942421e651e0b9a43e4b8b2c92e87a2552cede73e8380c9d79f411f742cad0c6f2b070aa08703a04cb7db840c3821a6762837dd8d00e9807dcfbc7f2fcc9415e2cb40eef7f718758d76193f325b3f8b7180e3e5e7d6b81e8036252cae6d24a531a151ce1ee223a07bf71cf82a7fdf49090e4ca345d27d68ca80e3f08ef11671f8f1defa66fa2af71e1a871430e9352df9b3f1d427b5a5dabfb280b51d28c9b99030d050fc1578cd23b5506e769b86c8f4ccc6aded4b8d7c1a73b7").to_vec(), + ].to_vec(); + let polkadot_state_root: H256 = hex!("81b75d95075d16005ee0a987a3f061d3011ada919b261e9b02961b9b3725f3fd").into(); + // As of query paras::heads(2_086) at block + // "0x18e90e9aa8e3b063f60386ba1b0415111798e72d01de58b1438d620d42f58e39" + // (16_363_919) which results in the key + // "0xcd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c32c0cfd6c23b92a7826080000" + let expected_spiritnet_head_at_block = hex!("65541097fb02782e14f43074f0b00e44ae8e9fe426982323ef1d329739740d37f252ff006d1156941db1bccd58ce3a1cac4f40cad91f692d94e98f501dd70081a129b69a3e2ef7e1ff84ba3d86dab4e95f2c87f6b1055ebd48519c185360eae58f05d1ea08066175726120dcdc6308000000000561757261010170ccfaf3756d1a8dd8ae5c89094199d6d32e5dd9f0920f6fe30f986815b5e701974ea0e0e0a901401f2c72e3dd8dbdf4aa55d59bf3e7021856cdb8038419eb8c").to_vec(); + let spiritnet_head_at_block = verify_storage_value_proof::<_, BlakeTwo256, HeadData>( + &spiritnet_head_storage_key, + polkadot_state_root, + spiritnet_head_proof_at_block, + ) + .expect( + "Merkle proof verification for pre-calculated Polkadot storage entry for Spiritnet head should not fail.", + ); + assert_eq!(expected_spiritnet_head_at_block, spiritnet_head_at_block.0); } } diff --git a/crates/kilt-dip-primitives/src/state_proofs/parachain.rs b/crates/kilt-dip-primitives/src/state_proofs/parachain.rs deleted file mode 100644 index b897770c5..000000000 --- a/crates/kilt-dip-primitives/src/state_proofs/parachain.rs +++ /dev/null @@ -1,168 +0,0 @@ -// KILT Blockchain – https://botlabs.org -// Copyright (C) 2019-2024 BOTLabs GmbH - -// The KILT Blockchain is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// The KILT Blockchain is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// If you feel like getting in touch with us, you can do so at info@botlabs.org - -use parity_scale_codec::Decode; -use sp_core::RuntimeDebug; -use sp_std::marker::PhantomData; -use sp_trie::StorageProof; - -use crate::{ - state_proofs::substrate_no_std_port::read_proof_check, traits::ProviderParachainStorageInfo, utils::OutputOf, -}; - -#[derive(RuntimeDebug)] -pub enum DipIdentityCommitmentProofVerifierError { - InvalidMerkleProof, - RequiredLeafNotRevealed, - CommitmentDecode, -} - -impl From for u8 { - fn from(value: DipIdentityCommitmentProofVerifierError) -> Self { - match value { - DipIdentityCommitmentProofVerifierError::InvalidMerkleProof => 0, - DipIdentityCommitmentProofVerifierError::RequiredLeafNotRevealed => 1, - DipIdentityCommitmentProofVerifierError::CommitmentDecode => 2, - } - } -} - -/// Verifier of state proofs that reveal the value of the DIP commitment for -/// a given subject on the provider chain. The generic types indicate the -/// following: -/// * `ParaInfo`: defines the provider parachain runtime types relevant for -/// state proof verification, and returns the provider's runtime storage key -/// identifying the identity commitment for a subject with the given -/// identifier. -pub struct DipIdentityCommitmentProofVerifier(PhantomData); - -impl DipIdentityCommitmentProofVerifier -where - ParaInfo: ProviderParachainStorageInfo, - OutputOf: Ord, - ParaInfo::Commitment: Decode, - ParaInfo::Key: AsRef<[u8]>, -{ - /// Given a parachain state root, verify a state proof for the - /// commitment of a given subject identifier. - #[cfg(not(feature = "runtime-benchmarks"))] - pub fn verify_proof_for_identifier( - identifier: &ParaInfo::Identifier, - state_root: OutputOf, - proof: impl IntoIterator>, - ) -> Result { - let dip_commitment_storage_key = ParaInfo::dip_subject_storage_key(identifier, 0); - let storage_proof = StorageProof::new(proof); - let revealed_leaves = - read_proof_check::(state_root, storage_proof, [&dip_commitment_storage_key].iter()) - .map_err(|_| DipIdentityCommitmentProofVerifierError::InvalidMerkleProof)?; - // TODO: Remove at some point - { - debug_assert!(revealed_leaves.len() == 1usize); - debug_assert!(revealed_leaves.contains_key(dip_commitment_storage_key.as_ref())); - } - let Some(Some(encoded_commitment)) = revealed_leaves.get(dip_commitment_storage_key.as_ref()) else { - return Err(DipIdentityCommitmentProofVerifierError::RequiredLeafNotRevealed); - }; - ParaInfo::Commitment::decode(&mut &encoded_commitment[..]) - .map_err(|_| DipIdentityCommitmentProofVerifierError::CommitmentDecode) - } - - #[cfg(feature = "runtime-benchmarks")] - pub fn verify_proof_for_identifier( - identifier: &ParaInfo::Identifier, - state_root: OutputOf, - proof: impl IntoIterator>, - ) -> Result - where - ParaInfo::Commitment: Default, - { - let dip_commitment_storage_key = ParaInfo::dip_subject_storage_key(identifier, 0); - let storage_proof = StorageProof::new(proof); - let revealed_leaves = - read_proof_check::(state_root, storage_proof, [&dip_commitment_storage_key].iter()) - .unwrap_or_default(); - let encoded_commitment = - if let Some(Some(encoded_commitment)) = revealed_leaves.get(dip_commitment_storage_key.as_ref()) { - encoded_commitment.clone() - } else { - sp_std::vec::Vec::default() - }; - let commitment = ParaInfo::Commitment::decode(&mut &encoded_commitment[..]).unwrap_or_default(); - Ok(commitment) - } -} - -#[cfg(test)] -mod spiritnet_test_event_count_value { - use super::*; - - use hex_literal::hex; - use pallet_dip_provider::IdentityCommitmentVersion; - use sp_core::{storage::StorageKey, H256}; - use sp_runtime::traits::BlakeTwo256; - - // Spiritnet block n: 4_184_668, - // hash 0x2c0746e7e9ccc6e4d27bcb4118cb6821ae53ae9bf372f4f49ac28d8598f9bed5 - struct StaticSpiritnetInfoProvider; - - // We use the `system::eventCount()` storage entry as a unit test here. - impl ProviderParachainStorageInfo for StaticSpiritnetInfoProvider { - type BlockNumber = u32; - // The type of the `eventCount()` storage entry. - type Commitment = u32; - type Hasher = BlakeTwo256; - // Irrelevant for this test here - type Identifier = (); - type Key = StorageKey; - - fn dip_subject_storage_key(_identifier: &Self::Identifier, _version: IdentityCommitmentVersion) -> Self::Key { - // system::eventCount() raw storage key - let storage_key = hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec(); - StorageKey(storage_key) - } - } - - #[test] - fn test_spiritnet_event_count() { - // As of RPC state_getReadProof(" - // 0x26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850", - // "0x2c0746e7e9ccc6e4d27bcb4118cb6821ae53ae9bf372f4f49ac28d8598f9bed5") - let spiritnet_event_count_proof_at_block = [ - hex!("800404645ea5c1b19ab7a04f536c519aca4983ac205cda3f0000000000545e98fdbe9ce6c55837576c60c7af38501005000000").to_vec(), - hex!("80401080481e2bd8085a02c5b58987bce7a69f0b5c7fa651e8e82c5481c94707860be9078067785103d453293707ba847e21df7e35a7a57b8fb929d40465328b6642669fcc").to_vec(), - hex!("80ffff8010623b5a3a9dbc752963d827be0bb855bf3e24258ae09341d5f762e96a836ac180c34b753605e821528756b55b4ddafb742df6e54fbc03ef401d4ebfd6dd4f3e44806f83646e0bf3ca0ac9f2092dea5b0e3caf210cc6b54c3b44a51855a133367a6580b02cde7b1fd3f8d13f698ef6e9daa29b32258d4d97a8947051070a4540aecacd80903d521961849d07ceee132617b8dde96c3ff472f5a9a089d4055ffe7ffd1e988016c29c943c106713bb8f16b776eb7daed005540165696da286cddf6b25d085448019a464010cb746b0589891f72b0eed603d4712b04af46f7bcae724564194801480a305ffe069db7eb21841f75b5939943f62c4abb3a051d530839c5dd935ccbc8a8035d8938b0c856878de1e3fe45a559588b2da52ccf195ab1e3d0aca6ac7bb079d8064019a474a283c19f46ff4652a5e1f636efd4013d3b8a91c49573045c6ff01c0801a191dcb736faddb84889a13c7aa717d260e9b635b30a9eb3907f925a2253d6880f8bc389fc62ca951609bae208b7506bae497623e647424062d1c56cb1f2d2e1c80211a9fb5f8b794f9fbfbdcd4519aa475ecaf9737b4ee513dde275d5fbbe64da080c267d0ead99634e9b9cfbf61a583877e0241ac518e62e909fbb017469de275f780b3059a7226d4b320c25e9b2f8ffe19cf93467e3b306885962c5f34b5671d15fe8092dfba9e30e1bbefab13c792755d06927e6141f7220b7485e5aa40de92401a66").to_vec(), - hex!("9eaa394eea5630e07c48ae0c9558cef7398f8069ef420a0deb5a428c9a08563b28a78874bba09124eecc8d28bf30b0e2ddd310745f04abf5cb34d6244378cddbf18e849d962c000000000736d8e8140100505f0e7b9012096b41c4eb3aaf947f6ea4290800004c5f0684a022a34dd8bfa2baaf44f172b710040180dd3270a03a1a13fc20bcdf24d1aa4ddccc6183db2e2e153b8a68ba8540699a8a80b413dad63538a591f7f2575d287520ee44d7143aa5ec2411969861e1f55a2989804c3f0f541a13980689894db7c60c785dd29e066f213bb29b17aa740682ad7efd8026d3a50544f5c89500745aca2be36cfe076f599c5115192fb9deae227e2710c980bd04b00bf6b42756a06a4fbf05a5231c2094e48182eca95d2cff73ab907592aa").to_vec(), - ].to_vec(); - let spiritnet_state_root: H256 = - hex!("94c23fda279cea4a4370e90f1544c8938923dfd4ac201a420c7a26fb0d3caf8c").into(); - // As of query system::eventCount() at block - // "0x2c0746e7e9ccc6e4d27bcb4118cb6821ae53ae9bf372f4f49ac28d8598f9bed5" which - // results in the key - // "0x26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850" - let expected_event_count_at_block = 5; - let returned_event_count = - DipIdentityCommitmentProofVerifier::::verify_proof_for_identifier( - &(), - spiritnet_state_root, - spiritnet_event_count_proof_at_block, - ) - .unwrap(); - assert!(returned_event_count == expected_event_count_at_block, "Spiritnet event count returned from the state proof verification should not be different than the pre-computed one."); - } -} diff --git a/crates/kilt-dip-primitives/src/state_proofs/relaychain.rs b/crates/kilt-dip-primitives/src/state_proofs/relaychain.rs deleted file mode 100644 index d27131b57..000000000 --- a/crates/kilt-dip-primitives/src/state_proofs/relaychain.rs +++ /dev/null @@ -1,261 +0,0 @@ -// KILT Blockchain – https://botlabs.org -// Copyright (C) 2019-2024 BOTLabs GmbH - -// The KILT Blockchain is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// The KILT Blockchain is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// If you feel like getting in touch with us, you can do so at info@botlabs.org - -use parity_scale_codec::{Encode, HasCompact}; -use sp_core::{storage::StorageKey, RuntimeDebug, U256}; -use sp_runtime::{generic::Header, traits::BlakeTwo256}; -use sp_std::{marker::PhantomData, vec::Vec}; -use sp_trie::StorageProof; - -use crate::{ - state_proofs::substrate_no_std_port::read_proof_check, - traits::{RelayChainStateInfo, RelayChainStorageInfo}, - utils::OutputOf, -}; - -#[derive(RuntimeDebug)] -pub enum ParachainHeadProofVerifierError { - InvalidMerkleProof, - RequiredLeafNotRevealed, - HeaderDecode, - RelaychainStateRootNotFound, -} - -impl From for u8 { - fn from(value: ParachainHeadProofVerifierError) -> Self { - match value { - ParachainHeadProofVerifierError::InvalidMerkleProof => 0, - ParachainHeadProofVerifierError::RequiredLeafNotRevealed => 1, - ParachainHeadProofVerifierError::HeaderDecode => 2, - ParachainHeadProofVerifierError::RelaychainStateRootNotFound => 3, - } - } -} - -/// Verifier of state proofs that reveal the value of a parachain head at a -/// given relaychain block number. -/// The generic types are the following: -/// * `RelayChainState`: defines the relaychain runtime types relevant for state -/// proof verification, and returns the relaychain runtime's storage key -/// identifying a parachain with a given ID. -pub struct ParachainHeadProofVerifier(PhantomData); - -// Uses the provided `root` to verify the proof. -impl ParachainHeadProofVerifier -where - RelayChainState: RelayChainStorageInfo, - OutputOf: Ord, - RelayChainState::BlockNumber: Copy + Into + TryFrom + HasCompact, - RelayChainState::Key: AsRef<[u8]>, -{ - /// Given a relaychain state root, verify a state proof for the - /// parachain with the provided ID. - #[cfg(not(feature = "runtime-benchmarks"))] - pub fn verify_proof_for_parachain_with_root( - para_id: &RelayChainState::ParaId, - root: &OutputOf<::Hasher>, - proof: impl IntoIterator>, - ) -> Result, ParachainHeadProofVerifierError> { - use parity_scale_codec::Decode; - - let parachain_storage_key = RelayChainState::parachain_head_storage_key(para_id); - let storage_proof = StorageProof::new(proof); - let revealed_leaves = - read_proof_check::(*root, storage_proof, [¶chain_storage_key].iter()) - .map_err(|_| ParachainHeadProofVerifierError::InvalidMerkleProof)?; - // TODO: Remove at some point - { - debug_assert!(revealed_leaves.len() == 1usize); - debug_assert!(revealed_leaves.contains_key(parachain_storage_key.as_ref())); - } - let Some(Some(encoded_head)) = revealed_leaves.get(parachain_storage_key.as_ref()) else { - return Err(ParachainHeadProofVerifierError::RequiredLeafNotRevealed); - }; - // TODO: Figure out why RPC call returns 2 bytes in front which we don't need - let mut unwrapped_head = &encoded_head[2..]; - Header::decode(&mut unwrapped_head).map_err(|_| ParachainHeadProofVerifierError::HeaderDecode) - } - - // Ignores any errors returned by the `read_proof_check` function and returns a - // default Header in case of failure. - #[cfg(feature = "runtime-benchmarks")] - pub fn verify_proof_for_parachain_with_root( - para_id: &RelayChainState::ParaId, - root: &OutputOf<::Hasher>, - proof: impl IntoIterator>, - ) -> Result, ParachainHeadProofVerifierError> { - use parity_scale_codec::Decode; - - let parachain_storage_key = RelayChainState::parachain_head_storage_key(para_id); - let storage_proof = StorageProof::new(proof); - let revealed_leaves = - read_proof_check::(*root, storage_proof, [¶chain_storage_key].iter()) - .unwrap_or_default(); - let encoded_head = if let Some(Some(encoded_head)) = revealed_leaves.get(parachain_storage_key.as_ref()) { - encoded_head.clone() - } else { - sp_std::vec![0u8; 3] - }; - let mut unwrapped_head = &encoded_head[2..]; - let header = Header::decode(&mut unwrapped_head).unwrap_or(Header { - number: U256::from(0u64) - .try_into() - .map_err(|_| "Block number should be created from a u64") - .unwrap(), - digest: Default::default(), - extrinsics_root: Default::default(), - parent_hash: Default::default(), - state_root: Default::default(), - }); - Ok(header) - } - - /// Given a relaychain state root provided by the `RelayChainState` - /// generic type, verify a state proof for the parachain with the - /// provided ID. - #[cfg(not(feature = "runtime-benchmarks"))] - pub fn verify_proof_for_parachain( - para_id: &RelayChainState::ParaId, - relay_height: &RelayChainState::BlockNumber, - proof: impl IntoIterator>, - ) -> Result, ParachainHeadProofVerifierError> - where - RelayChainState: RelayChainStateInfo, - { - let relay_state_root = RelayChainState::state_root_for_block(relay_height) - .ok_or(ParachainHeadProofVerifierError::RelaychainStateRootNotFound)?; - Self::verify_proof_for_parachain_with_root(para_id, &relay_state_root, proof) - } - - #[cfg(feature = "runtime-benchmarks")] - pub fn verify_proof_for_parachain( - para_id: &RelayChainState::ParaId, - relay_height: &RelayChainState::BlockNumber, - proof: impl IntoIterator>, - ) -> Result, ParachainHeadProofVerifierError> - where - RelayChainState: RelayChainStateInfo, - { - let relay_state_root = RelayChainState::state_root_for_block(relay_height).unwrap_or_default(); - Self::verify_proof_for_parachain_with_root(para_id, &relay_state_root, proof) - } -} - -/// Implementor of the [`RelayChainStorageInfo`] trait that return the state -/// root of a relaychain block with a given number by retrieving it from the -/// [`pallet_relay_store::Pallet`] pallet storage. It hardcodes the -/// relaychain `BlockNumber`, `Hasher`, `StorageKey`, and `ParaId` to the -/// ones used by Polkadot-based relaychains. This type cannot be used with -/// relaychains that adopt a different definition for any on those types. -pub struct RelayStateRootsViaRelayStorePallet(PhantomData); - -impl RelayChainStorageInfo for RelayStateRootsViaRelayStorePallet -where - Runtime: pallet_relay_store::Config, -{ - type BlockNumber = u32; - type Hasher = BlakeTwo256; - type Key = StorageKey; - type ParaId = u32; - - fn parachain_head_storage_key(para_id: &Self::ParaId) -> Self::Key { - // TODO: Access the runtime definition from here, once and if exposed. - let encoded_para_id = para_id.encode(); - let storage_key = [ - frame_support::storage::storage_prefix(b"Paras", b"Heads").as_slice(), - sp_io::hashing::twox_64(&encoded_para_id).as_slice(), - encoded_para_id.as_slice(), - ] - .concat(); - StorageKey(storage_key) - } -} - -impl RelayChainStateInfo for RelayStateRootsViaRelayStorePallet -where - Runtime: pallet_relay_store::Config, -{ - fn state_root_for_block(block_height: &Self::BlockNumber) -> Option> { - pallet_relay_store::Pallet::::latest_relay_head_for_block(block_height) - .map(|relay_header| relay_header.relay_parent_storage_root) - } -} - -#[cfg(test)] -mod polkadot_parachain_head_proof_verifier_tests { - use super::*; - - use hex_literal::hex; - use sp_runtime::traits::BlakeTwo256; - - // Polkadot block n: 16_363_919, - // hash 0x18e90e9aa8e3b063f60386ba1b0415111798e72d01de58b1438d620d42f58e39 - struct StaticPolkadotInfoProvider; - - impl RelayChainStorageInfo for StaticPolkadotInfoProvider { - type BlockNumber = u32; - type Hasher = BlakeTwo256; - type Key = StorageKey; - type ParaId = u32; - - fn parachain_head_storage_key(para_id: &Self::ParaId) -> Self::Key { - // Adapted from /~https://github.com/polytope-labs/substrate-ismp/blob/7fb09da6c7b818a98c25c962fee0ddde8e737306/parachain/src/consensus.rs#L369 - // Used for testing. In production this would be generated from the relay - // runtime definition of the `paras` storage map. - let encoded_para_id = para_id.encode(); - let storage_key = [ - frame_support::storage::storage_prefix(b"Paras", b"Heads").as_slice(), - sp_io::hashing::twox_64(&encoded_para_id).as_slice(), - encoded_para_id.as_slice(), - ] - .concat(); - StorageKey(storage_key) - } - } - - impl RelayChainStateInfo for StaticPolkadotInfoProvider { - fn state_root_for_block(_block_height: &Self::BlockNumber) -> Option> { - Some(hex!("81b75d95075d16005ee0a987a3f061d3011ada919b261e9b02961b9b3725f3fd").into()) - } - } - - #[test] - fn test_spiritnet_head_proof() { - // As of RPC state_getReadProof("0xcd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c32c0cfd6c23b92a7826080000", "0x18e90e9aa8e3b063f60386ba1b0415111798e72d01de58b1438d620d42f58e39") - let spiritnet_head_proof_at_block = [ - hex!("570c0cfd6c23b92a7826080000f102e90265541097fb02782e14f43074f0b00e44ae8e9fe426982323ef1d329739740d37f252ff006d1156941db1bccd58ce3a1cac4f40cad91f692d94e98f501dd70081a129b69a3e2ef7e1ff84ba3d86dab4e95f2c87f6b1055ebd48519c185360eae58f05d1ea08066175726120dcdc6308000000000561757261010170ccfaf3756d1a8dd8ae5c89094199d6d32e5dd9f0920f6fe30f986815b5e701974ea0e0e0a901401f2c72e3dd8dbdf4aa55d59bf3e7021856cdb8038419eb8c").to_vec(), - hex!("80046480186b1513c5112466ada33da3c65558979906ca9fb82510b62f6ea01f550a4807808bc90ded5636f31c8395a315b5f27a1a25a2ceebd36921a518669ce7e52f80e680993c5e952e6e4f72f295ba04951ace9029b23e9a87887b41895c16f77bec42ee80b798b224c5ee3d668519e75ca98504116f645fb969a5e2653a298b0181f9a694").to_vec(), - hex!("80ffff806ecd86e87715a007ee9b216d8a99a604773014260d51f6552b6fbd7c21786d9c80e23ef51809d6c80c01a6e264ff0d298cce01c1addfdbb0789597b9a6b3f3e4fd80c9c5f0f29d777e2cebcdbd06ddf1c2cfa8ee83524b37ace99d8b7a3aeff039b380da013185503cfefa6c9cc88751993f1f2bf4b8fa4918e876f499fb9405e3206c803a89668f636552a0fb93619913dcc46cf3e087363d532b76a345155a44a46b5180c2e7fc654720b7dcc0316ae1591fde4beb8b853a343b7e5e3ee564d2692c2ee280840f9c4ae7c16ae948828bf50faf062264402e6134d2d6144a5e3ecb0a1e1d9c80f93c2be1ef51fb2032445cc7fbc2023b9e3b8cf8c0d832b464ae48a020bfaa8c8010c63537c9bf58d50c8c0e13c154fd88b2f683e13701901bdc64565aa9b756d580f0b60eaf17fb680827e8a8938c717ac943b85ff373c0fc911e06c34a3a30327280ccb29f1efa59fd7c80a730cb88789a5a256b01fee7e83ac9a3c90da330adc7a480c8e57c547edb33d4b712f017f09d2de2e055f18815669c83eef2f7f3e5dcafae80b7b7e7ffc91a7dd4c4902f7f15cd7598d1258a75433ea953565661d114e2dcca80ebc3a2df819c7c2fd1a33eb1d484beaf7b71114d6a6db240d8b07dc10bfdc49b80a71f21aa3fa5d7475bf134d50f25e2515c797d0a4c2e93998888150c1f969ab8801e32613f54e70c95e9b16a14f5797522ef5e2ef7867868ff663e33e8880994ed").to_vec(), - hex!("9e710b30bd2eab0352ddcc26417aa1945fd380d49ebc7ca5c1b751c2badb5e5a326d3ba9e331d8b7c6cf279ed7fd71a8882b6c8038088652f73dc8a22336d10f492f0ef8836beaba0ccfeb0f8fabdc9df1d17e2d807f88402cbbed7fa3307e07044200b572d5e8e12913b41e1923dcb2c0799bc2be804d57e9a8e4934fab698a9db50682052ee9459c666a075d1bfc471da8e5da14da80b9aee043e378f8313e68a6030679ccf3880fa1e7ab19b6244b5c262b7a152f004c5f03c716fb8fff3de61a883bb76adb34a2040080f282bc12648ffb197ffc257edc7ff3a3fdda452daa51091ccbd2dfb91d8aa9518008a0c609ab4888f02c2545c002153297c2641c5a7b4f3d8e25c634e721f80bea80b6617c764df278313c426c46961ccde8ee7a03f9007b74bc8bc6c49d1583cf7d8077b493d45eb153353026cc330307e0753ac41a5cb8e843ceb1efdc46655f33a0808bdaa43fc5dc0e928e2da0ce8ed02096b0b74c61feaba2546980ed9c6174f71d").to_vec(), - hex!("9f0b3c252fcb29d88eff4f3de5de4476c3ffbf8013c601cc93de3437f9d415bd52c48d794b341f218b9d0020a4b646746c24d0ca80348b8e2c39c479a146933297f62b7051df82e92e1bca761432c3e6f64c74033f80220131e7cd7a08b97f8aa06225f7aefbbca8118fb436c07689c552ed3f577145806d974dd9e4db5e407e29f84c4121ccc58f9c6adc3933afc1bcaef52defe77de5801e9e1a21db053de56365fdee57998488ddae7d664c0430da90469dde17936c1f80c5c11751bbfc99a1ad805c58a65b9704e0bad58e694023e9cc57ce6ef84cdb0b8038f6c242700eaea04ffad5c25ca9a9b1cc2af7303655a32eb59e84b6bb927cd3802575469e76e104b0db8b18dbc762b997a78aa666432a44c4b955ced044a4691f80a81408b856272feeec08845af515e27d033efd3ff8b46de6bc706c38e600086a809ee78332c2a38a3918070942421e651e0b9a43e4b8b2c92e87a2552cede73e8380c9d79f411f742cad0c6f2b070aa08703a04cb7db840c3821a6762837dd8d00e9807dcfbc7f2fcc9415e2cb40eef7f718758d76193f325b3f8b7180e3e5e7d6b81e8036252cae6d24a531a151ce1ee223a07bf71cf82a7fdf49090e4ca345d27d68ca80e3f08ef11671f8f1defa66fa2af71e1a871430e9352df9b3f1d427b5a5dabfb280b51d28c9b99030d050fc1578cd23b5506e769b86c8f4ccc6aded4b8d7c1a73b7").to_vec(), - ].to_vec(); - // As of query paras::heads(2_086) at block - // "0x18e90e9aa8e3b063f60386ba1b0415111798e72d01de58b1438d620d42f58e39" - // (16_363_919) which results in the key - // "0xcd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c32c0cfd6c23b92a7826080000" - // - let expected_spiritnet_head_at_block = hex!("65541097fb02782e14f43074f0b00e44ae8e9fe426982323ef1d329739740d37f252ff006d1156941db1bccd58ce3a1cac4f40cad91f692d94e98f501dd70081a129b69a3e2ef7e1ff84ba3d86dab4e95f2c87f6b1055ebd48519c185360eae58f05d1ea08066175726120dcdc6308000000000561757261010170ccfaf3756d1a8dd8ae5c89094199d6d32e5dd9f0920f6fe30f986815b5e701974ea0e0e0a901401f2c72e3dd8dbdf4aa55d59bf3e7021856cdb8038419eb8c").to_vec(); - let returned_head = ParachainHeadProofVerifier::::verify_proof_for_parachain( - &2_086, - &16_363_919, - spiritnet_head_proof_at_block, - ) - .expect("Parachain head proof verification should not fail."); - assert!(returned_head.encode() == expected_spiritnet_head_at_block, "Parachain head returned from the state proof verification should not be different than the pre-computed one."); - } -} diff --git a/crates/kilt-dip-primitives/src/state_proofs/substrate_no_std_port.rs b/crates/kilt-dip-primitives/src/state_proofs/substrate_no_std_port.rs new file mode 100644 index 000000000..927927215 --- /dev/null +++ b/crates/kilt-dip-primitives/src/state_proofs/substrate_no_std_port.rs @@ -0,0 +1,69 @@ +// 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 hash_db::EMPTY_PREFIX; +use parity_scale_codec::Codec; +use sp_core::Hasher; +use sp_state_machine::{Backend, TrieBackend, TrieBackendBuilder}; +use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; +use sp_trie::{HashDBT, MemoryDB, StorageProof}; + +pub(super) fn read_proof_check( + root: H::Out, + proof: StorageProof, + keys: I, +) -> Result, Option>>, ()> +where + H: Hasher, + H::Out: Ord + Codec, + I: IntoIterator, + I::Item: AsRef<[u8]>, +{ + let proving_backend = create_proof_check_backend::(root, proof)?; + let mut result = BTreeMap::new(); + for key in keys.into_iter() { + let value = read_proof_check_on_proving_backend(&proving_backend, key.as_ref())?; + result.insert(key.as_ref().to_vec(), value); + } + Ok(result) +} + +fn read_proof_check_on_proving_backend( + proving_backend: &TrieBackend, H>, + key: &[u8], +) -> Result>, ()> +where + H: Hasher, + H::Out: Ord + Codec, +{ + proving_backend.storage(key).map_err(|_| ()) +} + +fn create_proof_check_backend(root: H::Out, proof: StorageProof) -> Result, H>, ()> +where + H: Hasher, + H::Out: Codec, +{ + let db = proof.into_memory_db(); + + if db.contains(&root, EMPTY_PREFIX) { + Ok(TrieBackendBuilder::new(db, root).build()) + } else { + Err(()) + } +} diff --git a/crates/kilt-dip-primitives/src/traits.rs b/crates/kilt-dip-primitives/src/traits.rs index 2c2f2c8fe..f6425ba16 100644 --- a/crates/kilt-dip-primitives/src/traits.rs +++ b/crates/kilt-dip-primitives/src/traits.rs @@ -16,14 +16,10 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use frame_system::pallet_prelude::BlockNumberFor; -use pallet_dip_provider::{IdentityCommitmentOf, IdentityCommitmentVersion}; -use sp_core::storage::StorageKey; +use sp_core::H256; use sp_runtime::traits::{CheckedAdd, One, Zero}; use sp_std::marker::PhantomData; -use crate::utils::OutputOf; - // TODO: Switch to the `Incrementable` trait once it's added to the root of // `frame_support`. /// A trait for "incrementable" types, i.e., types that have some notion of @@ -61,154 +57,56 @@ pub trait DipCallOriginFilter { fn check_call_origin_info(call: &Call, info: &Self::OriginInfo) -> Result; } -/// A trait that provides context (e.g., runtime type definitions, storage keys) -/// about the relaychain that is relevant for cross-chain state proofs. -pub trait RelayChainStorageInfo { - /// The type of relaychain block numbers. - type BlockNumber; - /// The type of the relaychain hashing algorithm. - type Hasher: sp_runtime::traits::Hash; - /// The type of the relaychain storage key. - type Key; - /// The type of parachain IDs. - type ParaId; - - /// Return the storage key pointing to the head of the parachain - /// identified by the provided ID. - fn parachain_head_storage_key(para_id: &Self::ParaId) -> Self::Key; -} - -/// A trait that provides state information about specific relaychain blocks. -pub trait RelayChainStateInfo: RelayChainStorageInfo { - /// Return the relaychain state root at a given block height. - fn state_root_for_block(block_height: &Self::BlockNumber) -> Option>; -} +/// A trait similar in functionality to the [`frame_support::traits::Get`], but +/// with an input argument and an associated return type. +pub trait GetWithArg { + type Result; -/// A trait that provides context (e.g., runtime type definitions, storage keys) -/// about the DIP provider parachain that is relevant for cross-chain state -/// proofs. -pub trait ProviderParachainStorageInfo { - /// The type of the provider chain's block numbers. - type BlockNumber; - /// The type of the provider chain's identity commitments. - type Commitment; - /// The type of the provider chain's storage keys. - type Key; - /// The type of the provider chain's hashing algorithm. - type Hasher: sp_runtime::traits::Hash; - /// The type of the provider chain's identity subject identifiers. - type Identifier; - - /// Return the storage key pointing to the identity commitment for the given - /// identifier and version. - fn dip_subject_storage_key(identifier: &Self::Identifier, version: IdentityCommitmentVersion) -> Self::Key; + fn get(arg: &Arg) -> Self::Result; } -/// Implementation of the [`ProviderParachainStorageInfo`] trait that builds on -/// the definitions of a runtime that includes the DIP provider pallet (e.g., -/// KILT runtimes). -/// The generic types are the following: -/// * `T`: The runtime including the [`pallet_dip_provider::Pallet`] pallet. -pub struct ProviderParachainStateInfoViaProviderPallet(PhantomData); +/// Implementer of the [`GetWithArg`] trait that return the state +/// root of a relaychain block with a given number by retrieving it from the +/// [`pallet_relay_store::Pallet`] pallet storage. It hardcodes the +/// relaychain `BlockNumber`, `Hasher`, `StorageKey`, and `ParaId` to the +/// ones used by Polkadot-based relaychains. This type cannot be used with +/// relaychains that adopt a different definition for any on those types. +pub struct RelayStateRootsViaRelayStorePallet(PhantomData); -impl ProviderParachainStorageInfo for ProviderParachainStateInfoViaProviderPallet +impl GetWithArg for RelayStateRootsViaRelayStorePallet where - T: pallet_dip_provider::Config, + Runtime: pallet_relay_store::Config, { - type BlockNumber = BlockNumberFor; - type Commitment = IdentityCommitmentOf; - type Hasher = T::Hashing; - type Identifier = T::Identifier; - type Key = StorageKey; - - fn dip_subject_storage_key(identifier: &Self::Identifier, version: IdentityCommitmentVersion) -> Self::Key { - StorageKey(pallet_dip_provider::IdentityCommitments::::hashed_key_for( - identifier, version, - )) - } -} - -/// A trait that provides the consumer parachain runtime additional context to -/// verify cross-chain DID signatures by subjects of the provider parachain. -pub trait DidSignatureVerifierContext { - /// Max number of blocks a cross-chain DID signature can have to be - /// considered fresh. - const SIGNATURE_VALIDITY: u16; - - /// The type of consumer parachain's block numbers. - type BlockNumber; - /// The type of consumer parachain's hashes. - type Hash; - /// Additional information that must be included in the payload being - /// DID-signed by the subject. - type SignedExtra; - - /// Returns the block number of the consumer's chain in which the DID - /// signature is being evaluated. - fn current_block_number() -> Self::BlockNumber; - /// Returns the genesis hash of the consumer's chain. - fn genesis_hash() -> Self::Hash; - /// Returns any additional info that must be appended to the payload before - /// verifying a cross-chain DID signature. - fn signed_extra() -> Self::SignedExtra; -} - -/// Implementation of the [`DidSignatureVerifierContext`] trait that draws -/// information dynamically from the consumer's runtime using its system pallet. -/// The generic types are the following: -/// * `T`: The runtime including the [`frame_system::Pallet`] pallet. -/// * `SIGNATURE_VALIDITY`: The max number of blocks DID signatures can have to -/// be considered valid. -pub struct FrameSystemDidSignatureContext(PhantomData); + type Result = Option; -impl DidSignatureVerifierContext - for FrameSystemDidSignatureContext -where - T: frame_system::Config, -{ - const SIGNATURE_VALIDITY: u16 = SIGNATURE_VALIDITY; - - type BlockNumber = BlockNumberFor; - type Hash = T::Hash; - type SignedExtra = (); - - fn current_block_number() -> Self::BlockNumber { - frame_system::Pallet::::block_number() + fn get(arg: &u32) -> Self::Result { + pallet_relay_store::Pallet::::latest_relay_head_for_block(arg) + .map(|relay_header| relay_header.relay_parent_storage_root) } +} - fn genesis_hash() -> Self::Hash { - frame_system::Pallet::::block_hash(Self::BlockNumber::zero()) - } +/// A trait similar in functionality to the [`frame_support::traits::Get`], but +/// with an associated return type. +pub trait GetWithoutArg { + type Result; - fn signed_extra() -> Self::SignedExtra {} + fn get() -> Self::Result; } -/// A trait that provides access to information on historical blocks. -pub trait HistoricalBlockRegistry { - /// The runtime definition of block numbers. - type BlockNumber; - /// The runtime hashing algorithm. - type Hasher: sp_runtime::traits::Hash; +impl GetWithoutArg for () { + type Result = (); - /// Retrieve a block hash given its number. - fn block_hash_for(block: &Self::BlockNumber) -> Option>; + fn get() -> Self::Result {} } -impl HistoricalBlockRegistry for T -where - T: frame_system::Config, -{ - type BlockNumber = BlockNumberFor; - type Hasher = T::Hashing; - - fn block_hash_for(block: &Self::BlockNumber) -> Option> { - let retrieved_block = frame_system::Pallet::::block_hash(block); - let default_block_hash_value = ::default(); - - if retrieved_block == default_block_hash_value { - None - } else { - Some(retrieved_block) - } - } -} +// Marker trait that requires a type to implement `Default` only for benchmarks. +// Avoids code duplication. +#[cfg(not(feature = "runtime-benchmarks"))] +pub trait BenchmarkDefault {} +#[cfg(not(feature = "runtime-benchmarks"))] +impl BenchmarkDefault for T {} + +#[cfg(feature = "runtime-benchmarks")] +pub trait BenchmarkDefault: Default {} +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkDefault for T {} diff --git a/crates/kilt-dip-primitives/src/utils.rs b/crates/kilt-dip-primitives/src/utils.rs index 042838eaa..7d71fb9cc 100644 --- a/crates/kilt-dip-primitives/src/utils.rs +++ b/crates/kilt-dip-primitives/src/utils.rs @@ -16,17 +16,18 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +use pallet_dip_provider::IdentityCommitmentVersion; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; -use sp_core::RuntimeDebug; -use sp_std::vec::Vec; +use sp_core::storage::StorageKey; +use sp_std::{fmt::Debug, vec::Vec}; /// The output of a type implementing the [`sp_runtime::traits::Hash`] trait. pub type OutputOf = ::Output; /// The vector of vectors that implements a statically-configured maximum length /// without requiring const generics, used in benchmarking worst cases. -#[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo, Clone)] +#[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Debug, TypeInfo, Clone)] pub struct BoundedBlindedValue(Vec>); impl BoundedBlindedValue { @@ -80,3 +81,26 @@ where Self(sp_std::vec![sp_std::vec![T::default(); 128]; 64]) } } + +pub(crate) fn calculate_parachain_head_storage_key(para_id: u32) -> StorageKey { + StorageKey( + [ + frame_support::storage::storage_prefix(b"Paras", b"Heads").as_slice(), + sp_io::hashing::twox_64(para_id.encode().as_ref()).as_slice(), + para_id.encode().as_slice(), + ] + .concat(), + ) +} + +pub(crate) fn calculate_dip_identity_commitment_storage_key_for_runtime( + subject: &Runtime::Identifier, + version: IdentityCommitmentVersion, +) -> StorageKey +where + Runtime: pallet_dip_provider::Config, +{ + StorageKey(pallet_dip_provider::IdentityCommitments::::hashed_key_for( + subject, version, + )) +} diff --git a/crates/kilt-dip-primitives/src/verifier/common.rs b/crates/kilt-dip-primitives/src/verifier/common.rs deleted file mode 100644 index 921c75e2f..000000000 --- a/crates/kilt-dip-primitives/src/verifier/common.rs +++ /dev/null @@ -1,77 +0,0 @@ -// KILT Blockchain – https://botlabs.org -// Copyright (C) 2019-2024 BOTLabs GmbH - -// The KILT Blockchain is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// The KILT Blockchain is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// If you feel like getting in touch with us, you can do so at info@botlabs.org - -pub mod latest { - pub use super::v0::{DipMerkleProofAndDidSignature, ParachainRootStateProof}; -} - -pub mod v0 { - use parity_scale_codec::{Decode, Encode}; - use scale_info::TypeInfo; - use sp_core::RuntimeDebug; - - use crate::{did::TimeBoundDidSignature, merkle::DidMerkleProof, BoundedBlindedValue}; - - #[derive(Encode, Decode, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, TypeInfo, Clone)] - pub struct ParachainRootStateProof { - /// The relaychain block height for which the proof has been generated. - pub(crate) relay_block_height: RelayBlockHeight, - /// The raw state proof. - pub(crate) proof: BoundedBlindedValue, - } - - #[cfg(feature = "runtime-benchmarks")] - impl kilt_support::traits::GetWorstCase - for ParachainRootStateProof - where - RelayBlockHeight: Default, - { - fn worst_case(context: Context) -> Self { - Self { - relay_block_height: RelayBlockHeight::default(), - proof: BoundedBlindedValue::worst_case(context), - } - } - } - - #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] - pub struct DipMerkleProofAndDidSignature { - /// The DIP Merkle proof revealing some leaves about the DID subject's - /// identity. - pub(crate) leaves: DidMerkleProof, - /// The cross-chain DID signature. - pub(crate) signature: TimeBoundDidSignature, - } - - #[cfg(feature = "runtime-benchmarks")] - impl kilt_support::traits::GetWorstCase - for DipMerkleProofAndDidSignature - where - BlindedValues: kilt_support::traits::GetWorstCase, - Leaf: Default + Clone, - BlockNumber: Default, - Context: Clone, - { - fn worst_case(context: Context) -> Self { - Self { - leaves: DidMerkleProof::worst_case(context.clone()), - signature: TimeBoundDidSignature::worst_case(context), - } - } - } -} diff --git a/crates/kilt-dip-primitives/src/verifier/mod.rs b/crates/kilt-dip-primitives/src/verifier/mod.rs index 4507a2f44..8ef00aad6 100644 --- a/crates/kilt-dip-primitives/src/verifier/mod.rs +++ b/crates/kilt-dip-primitives/src/verifier/mod.rs @@ -20,16 +20,9 @@ pub mod parachain; /// Verification logic to integrate a child chain as a DIP provider. pub mod relaychain; - -mod common; - pub use parachain::{ DipParachainStateProofVerifierError, KiltVersionedParachainVerifier, VersionedDipParachainStateProof, }; pub use relaychain::{ DipRelaychainStateProofVerifierError, KiltVersionedRelaychainVerifier, VersionedRelaychainStateProof, }; - -pub mod latest { - pub use super::{common::latest::*, parachain::latest::*, relaychain::latest::*}; -} diff --git a/crates/kilt-dip-primitives/src/verifier/parachain.rs b/crates/kilt-dip-primitives/src/verifier/parachain.rs index 97e1c4c80..817e4ad8f 100644 --- a/crates/kilt-dip-primitives/src/verifier/parachain.rs +++ b/crates/kilt-dip-primitives/src/verifier/parachain.rs @@ -16,415 +16,209 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use cumulus_primitives_core::ParaId; -use did::{did_details::DidVerificationKey, DidVerificationKeyRelationship, KeyIdOf}; -use frame_support::Parameter; -use frame_system::pallet_prelude::BlockNumberFor; +use did::KeyIdOf; +use frame_system::pallet_prelude::{BlockNumberFor, HeaderFor}; use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_dip_consumer::{traits::IdentityProofVerifier, RuntimeCallOf}; -use pallet_dip_provider::IdentityCommitmentOf; -use parity_scale_codec::{Decode, Encode, HasCompact}; +use pallet_dip_provider::traits::IdentityCommitmentGenerator; +use pallet_web3_names::Web3NameOf; +use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; -use sp_core::{RuntimeDebug, U256}; -use sp_runtime::traits::Get; -use sp_std::marker::PhantomData; +use sp_std::{fmt::Debug, marker::PhantomData}; use crate::{ - did::RevealedDidKeysSignatureAndCallVerifierError, - merkle::{DidMerkleProofVerifierError, RevealedDidMerkleProofLeaf, RevealedDidMerkleProofLeaves}, - state_proofs::{parachain::DipIdentityCommitmentProofVerifierError, relaychain::ParachainHeadProofVerifierError}, - traits::{self, DidSignatureVerifierContext, DipCallOriginFilter, Incrementable}, + merkle::v0::RevealedDidKey, + traits::{DipCallOriginFilter, GetWithArg, GetWithoutArg, Incrementable}, utils::OutputOf, - BoundedBlindedValue, FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet, + DipOriginInfo, Error, }; /// A KILT-specific DIP identity proof for a sibling consumer that supports /// versioning. /// /// For more info, refer to the version-specific proofs. -#[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] +#[derive(Encode, Decode, PartialEq, Eq, Debug, TypeInfo, Clone)] pub enum VersionedDipParachainStateProof< - RelayBlockHeight, - DipMerkleProofBlindedValues, - DipMerkleProofRevealedLeaf, - LocalBlockNumber, + RelayBlockNumber, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, > { V0( - v0::ParachainDipStateProof< - RelayBlockHeight, - DipMerkleProofBlindedValues, - DipMerkleProofRevealedLeaf, - LocalBlockNumber, + crate::merkle::v0::ParachainDipDidProof< + RelayBlockNumber, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, >, ), } #[cfg(feature = "runtime-benchmarks")] -impl - kilt_support::traits::GetWorstCase +impl< + RelayBlockNumber, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, + Context, + > kilt_support::traits::GetWorstCase for VersionedDipParachainStateProof< - RelayBlockHeight, - DipMerkleProofBlindedValues, - DipMerkleProofRevealedLeaf, - LocalBlockNumber, + RelayBlockNumber, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, + ConsumerBlockNumber, > where - RelayBlockHeight: Default, - DipMerkleProofBlindedValues: kilt_support::traits::GetWorstCase, - DipMerkleProofRevealedLeaf: Default + Clone, - LocalBlockNumber: Default, + RelayBlockNumber: Default, + KiltDidKeyId: Default + Clone, + KiltAccountId: Clone, + KiltBlockNumber: Default + Clone, + KiltWeb3Name: Clone, + KiltLinkableAccountId: Clone, + ConsumerBlockNumber: Default, Context: Clone, { fn worst_case(context: Context) -> Self { - Self::V0(v0::ParachainDipStateProof::worst_case(context)) + Self::V0(crate::merkle::v0::ParachainDipDidProof::worst_case(context)) } } -pub enum DipParachainStateProofVerifierError< - ParachainHeadMerkleProofVerificationError, - IdentityCommitmentMerkleProofVerificationError, - DipProofVerificationError, - DidSignatureVerificationError, -> { +pub enum DipParachainStateProofVerifierError { UnsupportedVersion, - ParachainHeadMerkleProof(ParachainHeadMerkleProofVerificationError), - IdentityCommitmentMerkleProof(IdentityCommitmentMerkleProofVerificationError), - DipProof(DipProofVerificationError), - DidSignature(DidSignatureVerificationError), + ProofComponentTooLarge(u8), + ProofVerification(Error), + DidOriginError(DidOriginError), + Internal, } -impl< - ParachainHeadMerkleProofVerificationError, - IdentityCommitmentMerkleProofVerificationError, - DipProofVerificationError, - DidSignatureVerificationError, - > - From< - DipParachainStateProofVerifierError< - ParachainHeadMerkleProofVerificationError, - IdentityCommitmentMerkleProofVerificationError, - DipProofVerificationError, - DidSignatureVerificationError, - >, - > for u16 +impl From> for u16 where - ParachainHeadMerkleProofVerificationError: Into, - IdentityCommitmentMerkleProofVerificationError: Into, - DipProofVerificationError: Into, - DidSignatureVerificationError: Into, + DidOriginError: Into, { - fn from( - value: DipParachainStateProofVerifierError< - ParachainHeadMerkleProofVerificationError, - IdentityCommitmentMerkleProofVerificationError, - DipProofVerificationError, - DidSignatureVerificationError, - >, - ) -> Self { + fn from(value: DipParachainStateProofVerifierError) -> Self { match value { - DipParachainStateProofVerifierError::UnsupportedVersion => 0, - DipParachainStateProofVerifierError::ParachainHeadMerkleProof(error) => { - u8::MAX as u16 + error.into() as u16 + // DO NOT USE 0 + // Errors of different sub-parts are separated by a `u8::MAX`. + // A value of 0 would make it confusing whether it's the previous sub-part error (u8::MAX) + // or the new sub-part error (u8::MAX + 0). + DipParachainStateProofVerifierError::UnsupportedVersion => 1, + DipParachainStateProofVerifierError::ProofComponentTooLarge(component_id) => { + u8::MAX as u16 + component_id as u16 } - DipParachainStateProofVerifierError::IdentityCommitmentMerkleProof(error) => { - u8::MAX as u16 * 2 + error.into() as u16 + DipParachainStateProofVerifierError::ProofVerification(error) => { + u8::MAX as u16 * 2 + u8::from(error) as u16 } - DipParachainStateProofVerifierError::DipProof(error) => u8::MAX as u16 * 3 + error.into() as u16, - DipParachainStateProofVerifierError::DidSignature(error) => u8::MAX as u16 * 4 + error.into() as u16, + DipParachainStateProofVerifierError::DidOriginError(error) => u8::MAX as u16 * 3 + error.into() as u16, + DipParachainStateProofVerifierError::Internal => u16::MAX, } } } -/// Proof verifier configured given a specific KILT runtime implementation. -/// -/// It is a specialization of the -/// [`GenericVersionedParachainVerifier`] type, with -/// configurations derived from the provided KILT runtime. -/// -/// The generic types -/// indicate the following: -/// * `KiltRuntime`: A KILT runtime definition. -/// * `KiltParachainId`: The ID of the specific KILT parachain instance. -/// * `RelayChainInfo`: The type providing information about the relaychain. -/// * `KiltDipMerkleHasher`: The hashing algorithm used by the KILT parachain -/// for the generation of the DIP identity commitment. -/// * `LocalDidCallVerifier`: Logic to map `RuntimeCall`s to a specific DID key -/// relationship. This information is used once the Merkle proof is verified, -/// to filter only the revealed keys that match the provided relationship. -/// * `MAX_REVEALED_KEYS_COUNT`: **OPTIONAL** Max number of DID keys that the -/// verifier will accept revealed as part of the DIP identity proof. It -/// defaults to **10**. -/// * `MAX_REVEALED_ACCOUNTS_COUNT`: **OPTIONAL** Max number of linked accounts -/// that the verifier will accept revealed as part of the DIP identity proof. -/// It defaults to **10**. -/// * `MAX_DID_SIGNATURE_DURATION`: **OPTIONAL** Max number of blocks a -/// cross-chain DID signature is considered fresh. It defaults to **50**. -/// -/// It specializes the [`GenericVersionedParachainVerifier`] -/// type by using the following types for its generics: -/// * `RelayChainInfo`: The provided `RelayChainInfo`. -/// * `ChildProviderParachainId`: The provided `KiltParachainId`. -/// * `ChildProviderStateInfo`: The -/// [`ProviderParachainStateInfoViaProviderPallet`] type configured with the -/// provided `KiltRuntime`. -/// * `ProviderDipMerkleHasher`: The provided `KiltDipMerkleHasher`. -/// * `ProviderDidKeyId`: The [`KeyIdOf`] type configured with the provided -/// `KiltRuntime`. -/// * `ProviderAccountId`: The `KiltRuntime::AccountId` type. -/// * `ProviderWeb3Name`: The `KiltRuntime::Web3Name` type. -/// * `ProviderLinkedAccountId`: The [`LinkableAccountId`] type. -/// * `MAX_REVEALED_KEYS_COUNT`: The provided `MAX_REVEALED_KEYS_COUNT`. -/// * `MAX_REVEALED_ACCOUNTS_COUNT`: The provided `MAX_REVEALED_ACCOUNTS_COUNT`. -/// * `LocalContextProvider`: The [`FrameSystemDidSignatureContext`] type -/// configured with the provided `KiltRuntime` and -/// `MAX_DID_SIGNATURE_DURATION`. -/// * `LocalDidCallVerifier`: The provided `LocalDidCallVerifier`. +/// Versioned proof verifier. For version-specific description, refer to each +/// verifier's documentation. pub struct KiltVersionedParachainVerifier< + RelaychainRuntime, + RelaychainStateRootStore, + const KILT_PARA_ID: u32, KiltRuntime, - KiltParachainId, - RelayChainStateInfo, - KiltDipMerkleHasher, - LocalDidCallVerifier, - const MAX_REVEALED_KEYS_COUNT: u32 = 10, - const MAX_REVEALED_ACCOUNTS_COUNT: u32 = 10, - const MAX_DID_SIGNATURE_DURATION: u16 = 50, + DidCallVerifier, + SignedExtra = (), + const MAX_PROVIDER_HEAD_PROOF_LEAVE_COUNT: u32 = 64, + const MAX_PROVIDER_HEAD_PROOF_LEAVE_SIZE: u32 = 1024, + const MAX_DIP_COMMITMENT_PROOF_LEAVE_COUNT: u32 = 64, + const MAX_DIP_COMMITMENT_PROOF_LEAVE_SIZE: u32 = 1024, + const MAX_DID_MERKLE_PROOF_LEAVE_COUNT: u32 = 64, + const MAX_DID_MERKLE_PROOF_LEAVE_SIZE: u32 = 1024, + const MAX_DID_MERKLE_LEAVES_REVEALED: u32 = 64, >( PhantomData<( + RelaychainRuntime, + RelaychainStateRootStore, KiltRuntime, - KiltParachainId, - RelayChainStateInfo, - KiltDipMerkleHasher, - LocalDidCallVerifier, + DidCallVerifier, + SignedExtra, )>, ); impl< ConsumerRuntime, + RelaychainRuntime, + RelaychainStateRootStore, + const KILT_PARA_ID: u32, KiltRuntime, - KiltParachainId, - RelayChainStateInfo, - KiltDipMerkleHasher, - LocalDidCallVerifier, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, - const MAX_DID_SIGNATURE_DURATION: u16, + DidCallVerifier, + SignedExtra, + const MAX_PROVIDER_HEAD_PROOF_LEAVE_COUNT: u32, + const MAX_PROVIDER_HEAD_PROOF_LEAVE_SIZE: u32, + const MAX_DIP_COMMITMENT_PROOF_LEAVE_COUNT: u32, + const MAX_DIP_COMMITMENT_PROOF_LEAVE_SIZE: u32, + const MAX_DID_MERKLE_PROOF_LEAVE_COUNT: u32, + const MAX_DID_MERKLE_PROOF_LEAVE_SIZE: u32, + const MAX_DID_MERKLE_LEAVES_REVEALED: u32, > IdentityProofVerifier for KiltVersionedParachainVerifier< + RelaychainRuntime, + RelaychainStateRootStore, + KILT_PARA_ID, KiltRuntime, - KiltParachainId, - RelayChainStateInfo, - KiltDipMerkleHasher, - LocalDidCallVerifier, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - MAX_DID_SIGNATURE_DURATION, + DidCallVerifier, + SignedExtra, + MAX_PROVIDER_HEAD_PROOF_LEAVE_COUNT, + MAX_PROVIDER_HEAD_PROOF_LEAVE_SIZE, + MAX_DIP_COMMITMENT_PROOF_LEAVE_COUNT, + MAX_DIP_COMMITMENT_PROOF_LEAVE_SIZE, + MAX_DID_MERKLE_PROOF_LEAVE_COUNT, + MAX_DID_MERKLE_PROOF_LEAVE_SIZE, + MAX_DID_MERKLE_LEAVES_REVEALED, > where - KiltRuntime: did::Config + ConsumerRuntime: pallet_dip_consumer::Config, + ConsumerRuntime::LocalIdentityInfo: Incrementable + Default, + RelaychainRuntime: frame_system::Config, + RelaychainStateRootStore: + GetWithArg, Result = Option>>, + KiltRuntime: frame_system::Config + + pallet_dip_provider::Config + + did::Config + pallet_web3_names::Config - + pallet_did_lookup::Config - + parachain_info::Config - + pallet_dip_provider::Config, - KiltParachainId: Get, - OutputOf: Ord + From>, - KeyIdOf: Into, - KiltDipMerkleHasher: sp_core::Hasher>, - ConsumerRuntime: pallet_dip_consumer::Config, - ConsumerRuntime::LocalIdentityInfo: Incrementable + Default + Encode, - RelayChainStateInfo: traits::RelayChainStorageInfo + traits::RelayChainStateInfo, - RelayChainStateInfo::ParaId: From, - RelayChainStateInfo::BlockNumber: Parameter + 'static + Copy + Into + TryFrom + HasCompact, - RelayChainStateInfo::Key: AsRef<[u8]>, - LocalDidCallVerifier: DipCallOriginFilter< + + pallet_did_lookup::Config, + KiltRuntime::IdentityCommitmentGenerator: + IdentityCommitmentGenerator, + SignedExtra: GetWithoutArg, + SignedExtra::Result: Encode, + DidCallVerifier: DipCallOriginFilter< RuntimeCallOf, - OriginInfo = ( - DidVerificationKey, - DidVerificationKeyRelationship, - ), + OriginInfo = RevealedDidKey, BlockNumberFor, KiltRuntime::AccountId>, >, + DidCallVerifier::Error: Into, { - type Error = DipParachainStateProofVerifierError< - ParachainHeadProofVerifierError, - DipIdentityCommitmentProofVerifierError, - DidMerkleProofVerifierError, - RevealedDidKeysSignatureAndCallVerifierError, - >; + type Error = DipParachainStateProofVerifierError; type Proof = VersionedDipParachainStateProof< - RelayChainStateInfo::BlockNumber, - BoundedBlindedValue, - RevealedDidMerkleProofLeaf< - KeyIdOf, - KiltRuntime::AccountId, - BlockNumberFor, - KiltRuntime::Web3Name, - LinkableAccountId, - >, - BlockNumberFor, - >; - type VerificationResult = RevealedDidMerkleProofLeaves< + BlockNumberFor, KeyIdOf, KiltRuntime::AccountId, BlockNumberFor, - KiltRuntime::Web3Name, + Web3NameOf, LinkableAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - >; - - fn verify_proof_for_call_against_details( - call: &RuntimeCallOf, - subject: &ConsumerRuntime::Identifier, - submitter: &ConsumerRuntime::AccountId, - identity_details: &mut Option, - proof: Self::Proof, - ) -> Result { - , - KiltDipMerkleHasher, - KeyIdOf, - KiltRuntime::AccountId, - KiltRuntime::Web3Name, - LinkableAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - FrameSystemDidSignatureContext, - LocalDidCallVerifier, - > as IdentityProofVerifier>::verify_proof_for_call_against_details( - call, - subject, - submitter, - identity_details, - proof, - ) - } -} - -/// Generic proof verifier for KILT-specific DIP identity proofs of different -/// versions coming from a sibling provider running one of the available KILT -/// runtimes. -/// -/// It expects the DIP proof to be a [`VersionedDipParachainStateProof`], -/// and returns [`RevealedDidMerkleProofLeaves`] if the proof is successfully -/// verified. -/// -/// For more info, refer to the version-specific proof identifiers. -pub struct GenericVersionedParachainVerifier< - RelayChainStateInfo, - SiblingProviderParachainId, - SiblingProviderStateInfo, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, - LocalContextProvider, - LocalDidCallVerifier, ->( - #[allow(clippy::type_complexity)] - PhantomData<( - RelayChainStateInfo, - SiblingProviderParachainId, - SiblingProviderStateInfo, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - LocalContextProvider, - LocalDidCallVerifier, - )>, -); - -impl< - ConsumerRuntime, - RelayChainStateInfo, - SiblingProviderParachainId, - SiblingProviderStateInfo, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, - LocalContextProvider, - LocalDidCallVerifier, - > IdentityProofVerifier - for GenericVersionedParachainVerifier< - RelayChainStateInfo, - SiblingProviderParachainId, - SiblingProviderStateInfo, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - LocalContextProvider, - LocalDidCallVerifier, - > where - ConsumerRuntime: pallet_dip_consumer::Config, - ConsumerRuntime::LocalIdentityInfo: Incrementable + Default, - - RelayChainStateInfo: traits::RelayChainStorageInfo + traits::RelayChainStateInfo, - OutputOf: Ord, - RelayChainStateInfo::BlockNumber: Parameter + 'static + Copy + Into + TryFrom + HasCompact, - RelayChainStateInfo::Key: AsRef<[u8]>, - - SiblingProviderParachainId: Get, - - SiblingProviderStateInfo: traits::ProviderParachainStorageInfo< - Identifier = ConsumerRuntime::Identifier, - Commitment = ProviderDipMerkleHasher::Out, - >, - OutputOf: Ord + From>, - SiblingProviderStateInfo::BlockNumber: Parameter + 'static, - SiblingProviderStateInfo::Commitment: Decode, - SiblingProviderStateInfo::Key: AsRef<[u8]>, - - LocalContextProvider: - DidSignatureVerifierContext, Hash = ConsumerRuntime::Hash>, - LocalContextProvider::SignedExtra: Encode, - LocalDidCallVerifier: DipCallOriginFilter< - RuntimeCallOf, - OriginInfo = (DidVerificationKey, DidVerificationKeyRelationship), - >, - - ProviderDipMerkleHasher: sp_core::Hasher, - ProviderDidKeyId: Parameter + 'static + Into, - ProviderAccountId: Parameter + 'static, - ProviderLinkedAccountId: Parameter + 'static, - ProviderWeb3Name: Parameter + 'static, -{ - type Error = DipParachainStateProofVerifierError< - ParachainHeadProofVerifierError, - DipIdentityCommitmentProofVerifierError, - DidMerkleProofVerifierError, - RevealedDidKeysSignatureAndCallVerifierError, - >; - type Proof = VersionedDipParachainStateProof< - RelayChainStateInfo::BlockNumber, - BoundedBlindedValue, - RevealedDidMerkleProofLeaf< - ProviderDidKeyId, - ProviderAccountId, - SiblingProviderStateInfo::BlockNumber, - ProviderWeb3Name, - ProviderLinkedAccountId, - >, BlockNumberFor, >; - type VerificationResult = RevealedDidMerkleProofLeaves< - ProviderDidKeyId, - ProviderAccountId, - SiblingProviderStateInfo::BlockNumber, - ProviderWeb3Name, - ProviderLinkedAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, + type VerificationResult = DipOriginInfo< + KeyIdOf, + KiltRuntime::AccountId, + BlockNumberFor, + Web3NameOf, + LinkableAccountId, + MAX_DID_MERKLE_LEAVES_REVEALED, >; fn verify_proof_for_call_against_details( @@ -436,18 +230,19 @@ impl< ) -> Result { match proof { VersionedDipParachainStateProof::V0(v0_proof) => as IdentityProofVerifier>::verify_proof_for_call_against_details( call, subject, @@ -459,310 +254,233 @@ impl< } } -pub mod latest { - pub use super::v0::ParachainDipStateProof; -} - pub mod v0 { use super::*; - use frame_support::Parameter; - use sp_std::borrow::Borrow; - - use crate::{ - did::{verify_did_signature_for_call, RevealedDidKeysAndSignature}, - merkle::verify_dip_merkle_proof, - state_proofs::{parachain::DipIdentityCommitmentProofVerifier, relaychain::ParachainHeadProofVerifier}, - traits::ProviderParachainStorageInfo, - verifier::common::v0::{DipMerkleProofAndDidSignature, ParachainRootStateProof}, - }; - - /// The expected format of a cross-chain DIP identity proof when the - /// identity information is bridged from a provider that is a sibling - /// of the chain where the information is consumed (i.e., consumer - /// chain). - #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] - pub struct ParachainDipStateProof< - RelayBlockHeight, - DipMerkleProofBlindedValues, - DipMerkleProofRevealedLeaf, - LocalBlockNumber, - > { - /// The state proof for the given parachain head. - pub(crate) para_state_root: ParachainRootStateProof, - /// The raw state proof for the DIP commitment of the given subject. - pub(crate) dip_identity_commitment: BoundedBlindedValue, - /// The cross-chain DID signature. - pub(crate) did: - DipMerkleProofAndDidSignature, - } + use frame_support::ensure; + use sp_runtime::{traits::Zero, SaturatedConversion}; - #[cfg(feature = "runtime-benchmarks")] - impl - kilt_support::traits::GetWorstCase - for ParachainDipStateProof - where - DipMerkleProofBlindedValues: kilt_support::traits::GetWorstCase, - DipMerkleProofRevealedLeaf: Default + Clone, - RelayBlockHeight: Default, - LocalBlockNumber: Default, - Context: Clone, - { - fn worst_case(context: Context) -> Self { - Self { - para_state_root: ParachainRootStateProof::worst_case(context.clone()), - dip_identity_commitment: BoundedBlindedValue::worst_case(context.clone()), - did: DipMerkleProofAndDidSignature::worst_case(context), - } - } - } + use crate::merkle::v0::ParachainDipDidProof; - /// Generic proof verifier for KILT-specific DIP identity proofs coming from - /// a sibling provider running one of the available KILT runtimes. - /// - /// The proof verification step is performed on every request, and this - /// specific verifier has no knowledge of caching or storing state about the - /// subject. It only takes the provided - /// `ConsumerRuntime::LocalIdentityInfo` and increases it if the proof is - /// successfully verified, to prevent replay attacks. If additional logic is - /// to be stored under the `ConsumerRuntime::LocalIdentityInfo` entry, a - /// different verifier or a wrapper around this verifier must be built. - /// - /// It expects the DIP proof to be a - /// [`VersionedDipParachainStateProof`], and returns - /// [`RevealedDidMerkleProofLeaves`] if the proof is successfully verified. - /// This information is then made availabe as an origin to the downstream - /// call dispatched. + /// Proof verifier configured given a specific KILT runtime implementation. /// - /// The verifier performs the following steps: - /// 1. Verifies the state proof about the state root of the relaychain block - /// at the provided height. The state root is provided by the - /// `RelayChainInfo` type. - /// 2. Verifies the state proof about the DIP commitment value on the - /// provider parachain at the block finalized at the given relaychain - /// block, using the relay state root validated in the previous step. - /// 3. Verifies the DIP Merkle proof revealing parts of the subject's DID - /// Document against the retrieved DIP commitment validated in the - /// previous step. - /// 4. Verifies the cross-chain DID signature over the payload composed by - /// the SCALE-encoded tuple of `(C, D, S, B, G, E)`, with: - /// * `C`: The `RuntimeCall` to dispatch after performing DIP - /// verification. - /// * `D`: The local details associated to the DID subject as stored in - /// the [`pallet_dip_consumer`] `IdentityEntries` storage map. - /// * `S`: The tx submitter's address. - /// * `B`: The block number of the consumer chain provided in the - /// cross-chain DID signature. - /// * `G`: The genesis hash of the consumer chain. - /// * `E`: Any additional information provided by the - /// `LocalContextProvider` implementation. /// The generic types /// indicate the following: - /// * `RelayChainInfo`: The type providing information about the relaychain. - /// * `SiblingProviderParachainId`: The parachain ID of the provider KILT - /// sibling parachain. - /// * `SiblingProviderStateInfo`: The type providing storage and state - /// information about the provider KILT sibling parachain. - /// * `ProviderDipMerkleHasher`: The hashing algorithm used by the KILT - /// parachain for the generation of the DIP identity commitment. - /// * `ProviderDidKeyId`: The runtime type of a DID key ID as defined by the - /// KILT child parachain. - /// * `ProviderAccountId`: The runtime type of an account ID as defined by - /// the KILT child parachain. - /// * `ProviderWeb3Name`: The runtime type of a web3name as defined by the - /// KILT child parachain. - /// * `ProviderLinkedAccountId`: The runtime type of a linked account ID as - /// defined by the KILT child parachain. - /// * `MAX_REVEALED_KEYS_COUNT`: Max number of DID keys that the verifier - /// will accept revealed as part of the DIP identity proof. - /// * `MAX_REVEALED_ACCOUNTS_COUNT`: Max number of linked accounts that the - /// verifier will accept revealed as part of the DIP identity proof. - /// * `LocalContextProvider`: The type providing context of the consumer - /// chain (e.g., current block number) for the sake of cross-chain DID - /// signature verification. - /// * `LocalDidCallVerifier`: Logic to map `RuntimeCall`s to a specific DID - /// key relationship. This information is used once the Merkle proof is + /// * `RelaychainRuntime`: The relaychain runtime definition. + /// * `RelaychainStateRootStore`: A type providing state roots for + /// relaychain blocks. + /// * `KILT_PARA_ID`: The ID of the specific KILT parachain instance. + /// * `KiltRuntime`: A KILT runtime definition. + /// * `DidCallVerifier`: Logic to map `RuntimeCall`s to a specific DID key + /// relationship. This information is used once the Merkle proof is /// verified, to filter only the revealed keys that match the provided /// relationship. - #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] + /// * `SignedExtra`: Any additional information that must be signed by the + /// DID subject in the cross-chain operation. + /// * `MAX_PROVIDER_HEAD_PROOF_LEAVE_COUNT`: The maximum number of leaves + /// that can be revealed as part of the parachain head storage proof. + /// * `MAX_PROVIDER_HEAD_PROOF_LEAVE_SIZE`: The maximum size of each leaf + /// revealed as part of the parachain head storage proof. + /// * `MAX_DIP_COMMITMENT_PROOF_LEAVE_COUNT`: The maximum number of leaves + /// that can be revealed as part of the DIP commitment storage proof. + /// * `MAX_DIP_COMMITMENT_PROOF_LEAVE_SIZE`: The maximum size of each leaf + /// revealed as part of the DIP commitment storage proof. + /// * `MAX_DID_MERKLE_PROOF_LEAVE_COUNT`: The maximum number of *blinded* + /// leaves that can be revealed as part of the DID Merkle proof. + /// * `MAX_DID_MERKLE_PROOF_LEAVE_SIZE`: The maximum size of each *blinded* + /// leaf revealed as part of the DID Merkle proof. + /// * `MAX_DID_MERKLE_LEAVES_REVEALED`: The maximum number of leaves that + /// can be revealed as part of the DID Merkle proof. pub struct ParachainVerifier< - RelayChainStateInfo, - SiblingProviderParachainId, - SiblingProviderStateInfo, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, - LocalContextProvider, - LocalDidCallVerifier, + RelaychainRuntime, + RelaychainStateRootStore, + const KILT_PARA_ID: u32, + KiltRuntime, + DidCallVerifier, + SignedExtra, + const MAX_PROVIDER_HEAD_PROOF_LEAVE_COUNT: u32, + const MAX_PROVIDER_HEAD_PROOF_LEAVE_SIZE: u32, + const MAX_DIP_COMMITMENT_PROOF_LEAVE_COUNT: u32, + const MAX_DIP_COMMITMENT_PROOF_LEAVE_SIZE: u32, + const MAX_DID_MERKLE_PROOF_LEAVE_COUNT: u32, + const MAX_DID_MERKLE_PROOF_LEAVE_SIZE: u32, + const MAX_DID_MERKLE_LEAVES_REVEALED: u32, >( - #[allow(clippy::type_complexity)] PhantomData<( - RelayChainStateInfo, - SiblingProviderParachainId, - SiblingProviderStateInfo, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - LocalContextProvider, - LocalDidCallVerifier, + RelaychainRuntime, + RelaychainStateRootStore, + KiltRuntime, + DidCallVerifier, + SignedExtra, )>, ); impl< ConsumerRuntime, - RelayChainStateInfo, - SiblingProviderParachainId, - SiblingProviderStateInfo, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, - LocalContextProvider, - LocalDidCallVerifier, + RelaychainRuntime, + RelaychainStateRootStore, + const KILT_PARA_ID: u32, + KiltRuntime, + DidCallVerifier, + SignedExtra, + const MAX_PROVIDER_HEAD_PROOF_LEAVE_COUNT: u32, + const MAX_PROVIDER_HEAD_PROOF_LEAVE_SIZE: u32, + const MAX_DIP_COMMITMENT_PROOF_LEAVE_COUNT: u32, + const MAX_DIP_COMMITMENT_PROOF_LEAVE_SIZE: u32, + const MAX_DID_MERKLE_PROOF_LEAVE_COUNT: u32, + const MAX_DID_MERKLE_PROOF_LEAVE_SIZE: u32, + const MAX_DID_MERKLE_LEAVES_REVEALED: u32, > IdentityProofVerifier for ParachainVerifier< - RelayChainStateInfo, - SiblingProviderParachainId, - SiblingProviderStateInfo, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - LocalContextProvider, - LocalDidCallVerifier, + RelaychainRuntime, + RelaychainStateRootStore, + KILT_PARA_ID, + KiltRuntime, + DidCallVerifier, + SignedExtra, + MAX_PROVIDER_HEAD_PROOF_LEAVE_COUNT, + MAX_PROVIDER_HEAD_PROOF_LEAVE_SIZE, + MAX_DIP_COMMITMENT_PROOF_LEAVE_COUNT, + MAX_DIP_COMMITMENT_PROOF_LEAVE_SIZE, + MAX_DID_MERKLE_PROOF_LEAVE_COUNT, + MAX_DID_MERKLE_PROOF_LEAVE_SIZE, + MAX_DID_MERKLE_LEAVES_REVEALED, > where - ConsumerRuntime: pallet_dip_consumer::Config, + ConsumerRuntime: pallet_dip_consumer::Config, ConsumerRuntime::LocalIdentityInfo: Incrementable + Default, - - RelayChainStateInfo: traits::RelayChainStorageInfo + traits::RelayChainStateInfo, - OutputOf: Ord, - RelayChainStateInfo::BlockNumber: Parameter + 'static + Copy + Into + TryFrom + HasCompact, - RelayChainStateInfo::Key: AsRef<[u8]>, - - SiblingProviderParachainId: Get, - - SiblingProviderStateInfo: traits::ProviderParachainStorageInfo< - Identifier = ConsumerRuntime::Identifier, - Commitment = ProviderDipMerkleHasher::Out, - >, - OutputOf: Ord + From>, - SiblingProviderStateInfo::BlockNumber: Parameter + 'static, - SiblingProviderStateInfo::Commitment: Decode, - SiblingProviderStateInfo::Key: AsRef<[u8]>, - - LocalContextProvider: - DidSignatureVerifierContext, Hash = ConsumerRuntime::Hash>, - LocalContextProvider::SignedExtra: Encode, - LocalDidCallVerifier: DipCallOriginFilter< + RelaychainRuntime: frame_system::Config, + RelaychainStateRootStore: + GetWithArg, Result = Option>>, + KiltRuntime: frame_system::Config + + pallet_dip_provider::Config + + did::Config + + pallet_web3_names::Config + + pallet_did_lookup::Config, + KiltRuntime::IdentityCommitmentGenerator: + IdentityCommitmentGenerator, + SignedExtra: GetWithoutArg, + SignedExtra::Result: Encode, + DidCallVerifier: DipCallOriginFilter< RuntimeCallOf, - OriginInfo = (DidVerificationKey, DidVerificationKeyRelationship), + OriginInfo = RevealedDidKey, BlockNumberFor, KiltRuntime::AccountId>, >, - - ProviderDipMerkleHasher: sp_core::Hasher, - ProviderDidKeyId: Parameter + 'static + Into, - ProviderAccountId: Parameter + 'static, - ProviderLinkedAccountId: Parameter + 'static, - ProviderWeb3Name: Parameter + 'static, + DidCallVerifier::Error: Into, { - type Error = DipParachainStateProofVerifierError< - ParachainHeadProofVerifierError, - DipIdentityCommitmentProofVerifierError, - DidMerkleProofVerifierError, - RevealedDidKeysSignatureAndCallVerifierError, - >; - type Proof = ParachainDipStateProof< - RelayChainStateInfo::BlockNumber, - BoundedBlindedValue, - RevealedDidMerkleProofLeaf< - ProviderDidKeyId, - ProviderAccountId, - SiblingProviderStateInfo::BlockNumber, - ProviderWeb3Name, - ProviderLinkedAccountId, - >, + type Error = DipParachainStateProofVerifierError; + type Proof = ParachainDipDidProof< + BlockNumberFor, + KeyIdOf, + KiltRuntime::AccountId, + BlockNumberFor, + Web3NameOf, + LinkableAccountId, BlockNumberFor, >; - type VerificationResult = RevealedDidMerkleProofLeaves< - ProviderDidKeyId, - ProviderAccountId, - SiblingProviderStateInfo::BlockNumber, - ProviderWeb3Name, - ProviderLinkedAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, + + type VerificationResult = DipOriginInfo< + KeyIdOf, + KiltRuntime::AccountId, + BlockNumberFor, + Web3NameOf, + LinkableAccountId, + MAX_DID_MERKLE_LEAVES_REVEALED, >; fn verify_proof_for_call_against_details( call: &RuntimeCallOf, - subject: &ConsumerRuntime::Identifier, - submitter: &ConsumerRuntime::AccountId, - identity_details: &mut Option, + subject: &::Identifier, + submitter: &::AccountId, + identity_details: &mut Option<::LocalIdentityInfo>, proof: Self::Proof, ) -> Result { // 1. Verify parachain state is finalized by relay chain and fresh. - let provider_parachain_header = - ParachainHeadProofVerifier::::verify_proof_for_parachain( - &SiblingProviderParachainId::get(), - &proof.para_state_root.relay_block_height, - proof.para_state_root.proof, + ensure!( + proof.provider_head_proof.proof.len() <= MAX_PROVIDER_HEAD_PROOF_LEAVE_COUNT.saturated_into(), + DipParachainStateProofVerifierError::ProofComponentTooLarge(0) + ); + ensure!( + proof + .provider_head_proof + .proof + .iter() + .all(|l| l.len() <= MAX_PROVIDER_HEAD_PROOF_LEAVE_SIZE.saturated_into()), + DipParachainStateProofVerifierError::ProofComponentTooLarge(1) + ); + let proof_without_relaychain = proof + .verify_provider_head_proof::>( + KILT_PARA_ID, ) - .map_err(DipParachainStateProofVerifierError::ParachainHeadMerkleProof)?; - - // 2. Verify commitment is included in provider parachain. - let subject_identity_commitment = - DipIdentityCommitmentProofVerifier::::verify_proof_for_identifier( - subject, - provider_parachain_header.state_root.into(), - proof.dip_identity_commitment, - ) - .map_err(DipParachainStateProofVerifierError::IdentityCommitmentMerkleProof)?; - - // 3. Verify DIP merkle proof. - let proof_leaves: RevealedDidMerkleProofLeaves< - ProviderDidKeyId, - ProviderAccountId, - ::BlockNumber, - ProviderWeb3Name, - ProviderLinkedAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - > = verify_dip_merkle_proof::< - ProviderDipMerkleHasher, - _, - _, - _, - _, - _, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - >(&subject_identity_commitment, proof.did.leaves) - .map_err(DipParachainStateProofVerifierError::DipProof)?; - - // 4. Verify call is signed by one of the DID keys revealed at step 3. - verify_did_signature_for_call::<_, _, _, _, LocalContextProvider, _, _, _, LocalDidCallVerifier>( + .map_err(DipParachainStateProofVerifierError::ProofVerification)?; + + // 2. Verify commitment is included in provider parachain state. + ensure!( + proof_without_relaychain.dip_commitment_proof.0.len() + <= MAX_DIP_COMMITMENT_PROOF_LEAVE_COUNT.saturated_into(), + DipParachainStateProofVerifierError::ProofComponentTooLarge(2) + ); + ensure!( + proof_without_relaychain + .dip_commitment_proof + .0 + .iter() + .all(|l| l.len() <= MAX_DIP_COMMITMENT_PROOF_LEAVE_SIZE.saturated_into()), + DipParachainStateProofVerifierError::ProofComponentTooLarge(3) + ); + let proof_without_parachain = proof_without_relaychain + .verify_dip_commitment_proof_for_subject::(subject) + .map_err(DipParachainStateProofVerifierError::ProofVerification)?; + + // 3. Verify DIP Merkle proof. + ensure!( + proof_without_parachain.dip_proof.blinded.len() <= MAX_DID_MERKLE_PROOF_LEAVE_COUNT.saturated_into(), + DipParachainStateProofVerifierError::ProofComponentTooLarge(4) + ); + ensure!( + proof_without_parachain + .dip_proof + .blinded + .iter() + .all(|l| l.len() <= MAX_DID_MERKLE_PROOF_LEAVE_SIZE.saturated_into()), + DipParachainStateProofVerifierError::ProofComponentTooLarge(5) + ); + let proof_without_dip_merkle = proof_without_parachain + .verify_dip_proof::() + .map_err(DipParachainStateProofVerifierError::ProofVerification)?; + + // 4. Verify call is signed by one of the DID keys revealed in the proof + let current_block_number = frame_system::Pallet::::block_number(); + let consumer_genesis_hash = + frame_system::Pallet::::block_hash(BlockNumberFor::::zero()); + let signed_extra = SignedExtra::get(); + let encoded_payload = ( call, + &identity_details, submitter, - identity_details, - RevealedDidKeysAndSignature { - merkle_leaves: proof_leaves.borrow(), - did_signature: proof.did.signature, - }, + proof_without_dip_merkle.signature.valid_until, + consumer_genesis_hash, + signed_extra, ) - .map_err(DipParachainStateProofVerifierError::DidSignature)?; - - Ok(proof_leaves) + .encode(); + let revealed_did_info = proof_without_dip_merkle + .verify_signature_time(¤t_block_number) + .and_then(|p| p.retrieve_signing_leaf_for_payload(&encoded_payload[..])) + .map_err(DipParachainStateProofVerifierError::ProofVerification)?; + + // 5. Verify the signing key fulfills the requirements + let signing_key = revealed_did_info + .get_signing_leaf() + .map_err(DipParachainStateProofVerifierError::ProofVerification)?; + DidCallVerifier::check_call_origin_info(call, signing_key) + .map_err(DipParachainStateProofVerifierError::DidOriginError)?; + + // 6. Increment the local details + if let Some(details) = identity_details { + details.increment(); + } else { + *identity_details = Some(Default::default()); + }; + + Ok(revealed_did_info) } } } diff --git a/crates/kilt-dip-primitives/src/verifier/relaychain.rs b/crates/kilt-dip-primitives/src/verifier/relaychain.rs index 7d26c2ae1..a97d2e7e1 100644 --- a/crates/kilt-dip-primitives/src/verifier/relaychain.rs +++ b/crates/kilt-dip-primitives/src/verifier/relaychain.rs @@ -16,435 +16,165 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use cumulus_primitives_core::ParaId; -use did::{did_details::DidVerificationKey, DidVerificationKeyRelationship, KeyIdOf}; -use frame_support::Parameter; +use did::KeyIdOf; use frame_system::pallet_prelude::BlockNumberFor; use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_dip_consumer::{traits::IdentityProofVerifier, RuntimeCallOf}; -use pallet_dip_provider::IdentityCommitmentOf; -use parity_scale_codec::{Codec, Decode, Encode, HasCompact}; +use pallet_dip_provider::{traits::IdentityCommitmentGenerator, IdentityCommitmentOf}; +use pallet_web3_names::Web3NameOf; +use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; -use sp_core::{RuntimeDebug, U256}; -use sp_runtime::traits::{AtLeast32BitUnsigned, Get, Hash, MaybeDisplay, Member, SimpleBitOps}; -use sp_std::marker::PhantomData; +use sp_core::U256; +use sp_runtime::traits::Hash; +use sp_std::{fmt::Debug, marker::PhantomData}; use crate::{ - did::RevealedDidKeysSignatureAndCallVerifierError, - merkle::{DidMerkleProofVerifierError, RevealedDidMerkleProofLeaf, RevealedDidMerkleProofLeaves}, - state_proofs::{parachain::DipIdentityCommitmentProofVerifierError, relaychain::ParachainHeadProofVerifierError}, - traits::{ - DidSignatureVerifierContext, DipCallOriginFilter, HistoricalBlockRegistry, Incrementable, - ProviderParachainStorageInfo, RelayChainStorageInfo, - }, + merkle::v0::RevealedDidKey, + traits::{DipCallOriginFilter, GetWithArg, GetWithoutArg, Incrementable}, utils::OutputOf, - BoundedBlindedValue, FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet, + DipOriginInfo, Error, }; /// A KILT-specific DIP identity proof for a parent consumer that supports /// versioning. /// /// For more info, refer to the version-specific proofs. -#[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] +#[derive(Encode, Decode, PartialEq, Eq, Debug, TypeInfo, Clone)] pub enum VersionedRelaychainStateProof< - ParentBlockHeight: Copy + Into + TryFrom, - ParentBlockHasher: Hash, - DipMerkleProofBlindedValues, - DipMerkleProofRevealedLeaf, + ConsumerBlockNumber: Copy + Into + TryFrom, + ConsumerBlockHasher: Hash, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, > { V0( - v0::RelaychainDipStateProof< - ParentBlockHeight, - ParentBlockHasher, - DipMerkleProofBlindedValues, - DipMerkleProofRevealedLeaf, + crate::merkle::v0::RelayDipDidProof< + ConsumerBlockNumber, + ConsumerBlockHasher, + KiltDidKeyId, + KiltAccountId, + KiltBlockNumber, + KiltWeb3Name, + KiltLinkableAccountId, >, ), } -pub enum DipRelaychainStateProofVerifierError< - ParachainHeadMerkleProofVerificationError, - IdentityCommitmentMerkleProofVerificationError, - DipProofVerificationError, - DidSignatureVerificationError, -> { +pub enum DipRelaychainStateProofVerifierError { UnsupportedVersion, - InvalidBlockHeight, - InvalidBlockHash, - ParachainHeadMerkleProof(ParachainHeadMerkleProofVerificationError), - IdentityCommitmentMerkleProof(IdentityCommitmentMerkleProofVerificationError), - DipProof(DipProofVerificationError), - DidSignature(DidSignatureVerificationError), + ProofComponentTooLarge(u8), + ProofVerification(Error), + DidOriginError(DidOriginError), + Internal, } -impl< - ParachainHeadMerkleProofVerificationError, - IdentityCommitmentMerkleProofVerificationError, - DipProofVerificationError, - DidSignatureVerificationError, - > - From< - DipRelaychainStateProofVerifierError< - ParachainHeadMerkleProofVerificationError, - IdentityCommitmentMerkleProofVerificationError, - DipProofVerificationError, - DidSignatureVerificationError, - >, - > for u16 +impl From> for u16 where - ParachainHeadMerkleProofVerificationError: Into, - IdentityCommitmentMerkleProofVerificationError: Into, - DipProofVerificationError: Into, - DidSignatureVerificationError: Into, + DidOriginError: Into, { - fn from( - value: DipRelaychainStateProofVerifierError< - ParachainHeadMerkleProofVerificationError, - IdentityCommitmentMerkleProofVerificationError, - DipProofVerificationError, - DidSignatureVerificationError, - >, - ) -> Self { + fn from(value: DipRelaychainStateProofVerifierError) -> Self { match value { - DipRelaychainStateProofVerifierError::UnsupportedVersion => 0, - DipRelaychainStateProofVerifierError::InvalidBlockHeight => 1, - DipRelaychainStateProofVerifierError::InvalidBlockHash => 2, - DipRelaychainStateProofVerifierError::ParachainHeadMerkleProof(error) => { - u8::MAX as u16 + error.into() as u16 + // DO NOT USE 0 + // Errors of different sub-parts are separated by a `u8::MAX`. + // A value of 0 would make it confusing whether it's the previous sub-part error (u8::MAX) + // or the new sub-part error (u8::MAX + 0). + DipRelaychainStateProofVerifierError::UnsupportedVersion => 1, + DipRelaychainStateProofVerifierError::ProofComponentTooLarge(component_id) => { + u8::MAX as u16 + component_id as u16 } - DipRelaychainStateProofVerifierError::IdentityCommitmentMerkleProof(error) => { - u8::MAX as u16 * 2 + error.into() as u16 + DipRelaychainStateProofVerifierError::ProofVerification(error) => { + u8::MAX as u16 * 2 + u8::from(error) as u16 } - DipRelaychainStateProofVerifierError::DipProof(error) => u8::MAX as u16 * 3 + error.into() as u16, - DipRelaychainStateProofVerifierError::DidSignature(error) => u8::MAX as u16 * 4 + error.into() as u16, + DipRelaychainStateProofVerifierError::DidOriginError(error) => u8::MAX as u16 * 3 + error.into() as u16, + DipRelaychainStateProofVerifierError::Internal => u16::MAX, } } } -/// Proof verifier configured given a specific KILT runtime implementation. -/// -/// A specialization of the -/// [`GenericVersionedRelaychainVerifier`] type, with -/// configurations derived from the provided KILT runtime. -/// -/// The generic types are the following: -/// * `KiltRuntime`: A KILT runtime definition. -/// * `KiltParachainId`: The ID of the specific KILT parachain instance. -/// * `RelayChainInfo`: The type providing information about the consumer -/// (relay)chain. -/// * `KiltDipMerkleHasher`: The hashing algorithm used by the KILT parachain -/// for the generation of the DIP identity commitment. -/// * `LocalDidCallVerifier`: Logic to map `RuntimeCall`s to a specific DID key -/// relationship. This information is used once the Merkle proof is verified, -/// to filter only the revealed keys that match the provided relationship. -/// * `MAX_REVEALED_KEYS_COUNT`: **OPTIONAL** Max number of DID keys that the -/// verifier will accept revealed as part of the DIP identity proof. It -/// defaults to **10**. -/// * `MAX_REVEALED_ACCOUNTS_COUNT`: **OPTIONAL** Max number of linked accounts -/// that the verifier will accept revealed as part of the DIP identity proof. -/// It defaults to **10**. -/// * `MAX_DID_SIGNATURE_DURATION`: **OPTIONAL** Max number of blocks a -/// cross-chain DID signature is considered fresh. It defaults to **50**. -/// -/// It specializes the [`GenericVersionedRelaychainVerifier`] -/// type by using the following types for its generics: -/// * `RelayChainInfo`: The provided `RelayChainInfo`. -/// * `ChildProviderParachainId`: The provided `KiltParachainId`. -/// * `ChildProviderStateInfo`: The -/// [`ProviderParachainStateInfoViaProviderPallet`] type configured with the -/// provided `KiltRuntime`. -/// * `ProviderDipMerkleHasher`: The provided `KiltDipMerkleHasher`. -/// * `ProviderDidKeyId`: The [`KeyIdOf`] type configured with the provided -/// `KiltRuntime`. -/// * `ProviderAccountId`: The `KiltRuntime::AccountId` type. -/// * `ProviderWeb3Name`: The `KiltRuntime::Web3Name` type. -/// * `ProviderLinkedAccountId`: The [`LinkableAccountId`] type. -/// * `MAX_REVEALED_KEYS_COUNT`: The provided `MAX_REVEALED_KEYS_COUNT`. -/// * `MAX_REVEALED_ACCOUNTS_COUNT`: The provided `MAX_REVEALED_ACCOUNTS_COUNT`. -/// * `LocalContextProvider`: The [`FrameSystemDidSignatureContext`] type -/// configured with the provided `KiltRuntime` and -/// `MAX_DID_SIGNATURE_DURATION`. -/// * `LocalDidCallVerifier`: The provided `LocalDidCallVerifier`. +/// Versioned proof verifier. For version-specific description, refer to each +/// verifier's documentation. pub struct KiltVersionedRelaychainVerifier< + ConsumerBlockHashStore, + const KILT_PARA_ID: u32, KiltRuntime, - KiltParachainId, - RelayChainInfo, - KiltDipMerkleHasher, - LocalDidCallVerifier, - const MAX_REVEALED_KEYS_COUNT: u32 = 10, - const MAX_REVEALED_ACCOUNTS_COUNT: u32 = 10, - const MAX_DID_SIGNATURE_DURATION: u16 = 50, ->( - PhantomData<( - KiltRuntime, - KiltParachainId, - RelayChainInfo, - KiltDipMerkleHasher, - LocalDidCallVerifier, - )>, -); + DidCallVerifier, + SignedExtra = (), + const MAX_PROVIDER_HEAD_PROOF_LEAVE_COUNT: u32 = 64, + const MAX_PROVIDER_HEAD_PROOF_LEAVE_SIZE: u32 = 128, + const MAX_DIP_COMMITMENT_PROOF_LEAVE_COUNT: u32 = 64, + const MAX_DIP_COMMITMENT_PROOF_LEAVE_SIZE: u32 = 128, + const MAX_DID_MERKLE_PROOF_LEAVE_COUNT: u32 = 64, + const MAX_DID_MERKLE_PROOF_LEAVE_SIZE: u32 = 128, + const MAX_DID_MERKLE_LEAVES_REVEALED: u32 = 64, +>(#[allow(clippy::type_complexity)] PhantomData<(ConsumerBlockHashStore, KiltRuntime, DidCallVerifier, SignedExtra)>); impl< ConsumerRuntime, + ConsumerBlockHashStore, + const KILT_PARA_ID: u32, KiltRuntime, - KiltParachainId, - RelayChainInfo, - KiltDipMerkleHasher, - LocalDidCallVerifier, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, - const MAX_DID_SIGNATURE_DURATION: u16, + DidCallVerifier, + SignedExtra, + const MAX_PROVIDER_HEAD_PROOF_LEAVE_COUNT: u32, + const MAX_PROVIDER_HEAD_PROOF_LEAVE_SIZE: u32, + const MAX_DIP_COMMITMENT_PROOF_LEAVE_COUNT: u32, + const MAX_DIP_COMMITMENT_PROOF_LEAVE_SIZE: u32, + const MAX_DID_MERKLE_PROOF_LEAVE_COUNT: u32, + const MAX_DID_MERKLE_PROOF_LEAVE_SIZE: u32, + const MAX_DID_MERKLE_LEAVES_REVEALED: u32, > IdentityProofVerifier for KiltVersionedRelaychainVerifier< + ConsumerBlockHashStore, + KILT_PARA_ID, KiltRuntime, - KiltParachainId, - RelayChainInfo, - KiltDipMerkleHasher, - LocalDidCallVerifier, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - MAX_DID_SIGNATURE_DURATION, + DidCallVerifier, + SignedExtra, + MAX_PROVIDER_HEAD_PROOF_LEAVE_COUNT, + MAX_PROVIDER_HEAD_PROOF_LEAVE_SIZE, + MAX_DIP_COMMITMENT_PROOF_LEAVE_COUNT, + MAX_DIP_COMMITMENT_PROOF_LEAVE_SIZE, + MAX_DID_MERKLE_PROOF_LEAVE_COUNT, + MAX_DID_MERKLE_PROOF_LEAVE_SIZE, + MAX_DID_MERKLE_LEAVES_REVEALED, > where - KiltRuntime: did::Config + ConsumerRuntime: pallet_dip_consumer::Config, + ConsumerRuntime::LocalIdentityInfo: Incrementable + Default, + BlockNumberFor: Into + TryFrom, + ConsumerBlockHashStore: + GetWithArg, Result = Option>>, + KiltRuntime: frame_system::Config + + pallet_dip_provider::Config + + did::Config + pallet_web3_names::Config - + pallet_did_lookup::Config - + parachain_info::Config - + pallet_dip_provider::Config, - KiltParachainId: Get, - OutputOf: Ord + From::Hasher>>, - KeyIdOf: Into, - KiltDipMerkleHasher: sp_core::Hasher>, - ConsumerRuntime: pallet_dip_consumer::Config, - ConsumerRuntime::LocalIdentityInfo: Incrementable + Default + Encode, - RelayChainInfo: RelayChainStorageInfo> - + HistoricalBlockRegistry< - BlockNumber = ::BlockNumber, - Hasher = ::Hasher, - >, - RelayChainInfo::ParaId: From, - OutputOf<::Hasher>: - Ord + Default + sp_std::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec, - ::Hasher: Parameter + 'static, - ::BlockNumber: Copy - + Into - + TryFrom - + HasCompact - + Member - + sp_std::hash::Hash - + MaybeDisplay - + AtLeast32BitUnsigned - + Codec - + Parameter - + 'static, - RelayChainInfo::Key: AsRef<[u8]>, - LocalDidCallVerifier: DipCallOriginFilter< + + pallet_did_lookup::Config, + KiltRuntime::IdentityCommitmentGenerator: IdentityCommitmentGenerator, + SignedExtra: GetWithoutArg, + SignedExtra::Result: Encode, + DidCallVerifier: DipCallOriginFilter< RuntimeCallOf, - OriginInfo = ( - DidVerificationKey, - DidVerificationKeyRelationship, - ), + OriginInfo = RevealedDidKey, BlockNumberFor, KiltRuntime::AccountId>, >, + DidCallVerifier::Error: Into, { - type Error = DipRelaychainStateProofVerifierError< - ParachainHeadProofVerifierError, - DipIdentityCommitmentProofVerifierError, - DidMerkleProofVerifierError, - RevealedDidKeysSignatureAndCallVerifierError, - >; + type Error = DipRelaychainStateProofVerifierError; type Proof = VersionedRelaychainStateProof< - ::BlockNumber, - ::Hasher, - BoundedBlindedValue, - RevealedDidMerkleProofLeaf< - KeyIdOf, - KiltRuntime::AccountId, - BlockNumberFor, - KiltRuntime::Web3Name, - LinkableAccountId, - >, - >; - type VerificationResult = RevealedDidMerkleProofLeaves< + BlockNumberFor, + ConsumerRuntime::Hashing, KeyIdOf, KiltRuntime::AccountId, BlockNumberFor, - KiltRuntime::Web3Name, + Web3NameOf, LinkableAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - >; - - fn verify_proof_for_call_against_details( - call: &RuntimeCallOf, - subject: &ConsumerRuntime::Identifier, - submitter: &ConsumerRuntime::AccountId, - identity_details: &mut Option, - proof: Self::Proof, - ) -> Result { - , - KiltDipMerkleHasher, - KeyIdOf, - KiltRuntime::AccountId, - KiltRuntime::Web3Name, - LinkableAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - FrameSystemDidSignatureContext, - LocalDidCallVerifier, - > as IdentityProofVerifier>::verify_proof_for_call_against_details( - call, - subject, - submitter, - identity_details, - proof, - ) - } -} - -/// Generic proof verifier for KILT-specific DIP identity proofs of different -/// versions coming from a child provider running one of the available KILT -/// runtimes. -/// -/// It expects the DIP proof to be a [`VersionedRelaychainStateProof`], -/// and returns [`RevealedDidMerkleProofLeaves`] if the proof is successfully -/// verified. -/// -/// For more info, refer to the version-specific proof identifiers. -pub struct GenericVersionedRelaychainVerifier< - RelayChainInfo, - ChildProviderParachainId, - ChildProviderStateInfo, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, - LocalContextProvider, - LocalDidCallVerifier, ->( - #[allow(clippy::type_complexity)] - PhantomData<( - RelayChainInfo, - ChildProviderParachainId, - ChildProviderStateInfo, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - LocalContextProvider, - LocalDidCallVerifier, - )>, -); - -impl< - ConsumerRuntime, - RelayChainInfo, - ChildProviderParachainId, - ChildProviderStateInfo, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, - LocalContextProvider, - LocalDidCallVerifier, - > IdentityProofVerifier - for GenericVersionedRelaychainVerifier< - RelayChainInfo, - ChildProviderParachainId, - ChildProviderStateInfo, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - LocalContextProvider, - LocalDidCallVerifier, - > where - ConsumerRuntime: pallet_dip_consumer::Config, - ConsumerRuntime::LocalIdentityInfo: Incrementable + Default, - - RelayChainInfo: RelayChainStorageInfo> - + HistoricalBlockRegistry< - BlockNumber = ::BlockNumber, - Hasher = ::Hasher, - >, - OutputOf<::Hasher>: - Ord + Default + sp_std::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec, - ::Hasher: Parameter + 'static, - ::BlockNumber: Copy - + Into - + TryFrom - + HasCompact - + Member - + sp_std::hash::Hash - + MaybeDisplay - + AtLeast32BitUnsigned - + Codec - + Parameter - + 'static, - RelayChainInfo::Key: AsRef<[u8]>, - - ChildProviderParachainId: Get, - - ChildProviderStateInfo: ProviderParachainStorageInfo< - Identifier = ConsumerRuntime::Identifier, - Commitment = ProviderDipMerkleHasher::Out, - >, - OutputOf: Ord + From::Hasher>>, - ChildProviderStateInfo::BlockNumber: Parameter + 'static, - ChildProviderStateInfo::Commitment: Decode, - ChildProviderStateInfo::Key: AsRef<[u8]>, - - LocalContextProvider: - DidSignatureVerifierContext, Hash = ConsumerRuntime::Hash>, - LocalContextProvider::SignedExtra: Encode, - LocalDidCallVerifier: DipCallOriginFilter< - RuntimeCallOf, - OriginInfo = (DidVerificationKey, DidVerificationKeyRelationship), - >, - - ProviderDipMerkleHasher: sp_core::Hasher, - ProviderDidKeyId: Parameter + 'static + Into, - ProviderAccountId: Parameter + 'static, - ProviderLinkedAccountId: Parameter + 'static, - ProviderWeb3Name: Parameter + 'static, -{ - type Error = DipRelaychainStateProofVerifierError< - ParachainHeadProofVerifierError, - DipIdentityCommitmentProofVerifierError, - DidMerkleProofVerifierError, - RevealedDidKeysSignatureAndCallVerifierError, >; - type Proof = VersionedRelaychainStateProof< - ::BlockNumber, - ::Hasher, - BoundedBlindedValue, - RevealedDidMerkleProofLeaf< - ProviderDidKeyId, - ProviderAccountId, - ChildProviderStateInfo::BlockNumber, - ProviderWeb3Name, - ProviderLinkedAccountId, - >, - >; - type VerificationResult = RevealedDidMerkleProofLeaves< - ProviderDidKeyId, - ProviderAccountId, - ChildProviderStateInfo::BlockNumber, - ProviderWeb3Name, - ProviderLinkedAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, + type VerificationResult = DipOriginInfo< + KeyIdOf, + KiltRuntime::AccountId, + BlockNumberFor, + Web3NameOf, + LinkableAccountId, + MAX_DID_MERKLE_LEAVES_REVEALED, >; fn verify_proof_for_call_against_details( @@ -456,18 +186,18 @@ impl< ) -> Result { match proof { VersionedRelaychainStateProof::V0(v0_proof) => as IdentityProofVerifier>::verify_proof_for_call_against_details( call, subject, @@ -479,264 +209,128 @@ impl< } } -pub mod latest { - pub use super::v0::RelaychainDipStateProof; -} - pub mod v0 { use super::*; - use parity_scale_codec::Codec; - use sp_runtime::{ - generic::Header, - traits::{AtLeast32BitUnsigned, Hash, MaybeDisplay, Member, SimpleBitOps}, - }; - use sp_std::{borrow::Borrow, vec::Vec}; + use frame_support::ensure; + use frame_system::pallet_prelude::HeaderFor; + use sp_runtime::{traits::Zero, SaturatedConversion}; - use crate::{ - did::{ - verify_did_signature_for_call, RevealedDidKeysAndSignature, RevealedDidKeysSignatureAndCallVerifierError, - }, - merkle::{ - verify_dip_merkle_proof, DidMerkleProofVerifierError, RevealedDidMerkleProofLeaf, - RevealedDidMerkleProofLeaves, - }, - state_proofs::{ - parachain::{DipIdentityCommitmentProofVerifier, DipIdentityCommitmentProofVerifierError}, - relaychain::{ParachainHeadProofVerifier, ParachainHeadProofVerifierError}, - }, - traits::{ - DidSignatureVerifierContext, DipCallOriginFilter, HistoricalBlockRegistry, Incrementable, - ProviderParachainStorageInfo, RelayChainStorageInfo, - }, - utils::OutputOf, - verifier::common::v0::{DipMerkleProofAndDidSignature, ParachainRootStateProof}, - }; + use crate::RelayDipDidProof; - /// The expected format of a cross-chain DIP identity proof when the - /// identity information is bridged from a provider that is a child of - /// the chain where the information is consumed (i.e., consumer - /// chain). - #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] - pub struct RelaychainDipStateProof< - ParentBlockHeight: Copy + Into + TryFrom, - ParentBlockHasher: Hash, - DipMerkleProofBlindedValues, - DipMerkleProofRevealedLeaf, - > { - /// The state proof for the given parachain head. - para_state_root: ParachainRootStateProof, - /// The relaychain header for the relaychain block specified in the - /// `para_state_root`. - relay_header: Header, - /// The raw state proof for the DIP commitment of the given subject. - dip_identity_commitment: Vec>, - /// The cross-chain DID signature. - did: DipMerkleProofAndDidSignature, - } - - /// Generic proof verifier for KILT-specific DIP identity proofs coming from - /// a child provider running one of the available KILT runtimes. - /// The proof verification step is performed on every request, and this - /// specific verifier has no knowledge of caching or storing state about the - /// subject. It only takes the provided - /// `ConsumerRuntime::LocalIdentityInfo` and increases it if the proof is - /// successfully verified, to prevent replay attacks. If additional logic is - /// to be stored under the `ConsumerRuntime::LocalIdentityInfo` entry, a - /// different verifier or a wrapper around this verifier must be built. + /// Proof verifier configured given a specific KILT runtime implementation. /// - /// It expects the DIP proof to be a - /// [`VersionedRelaychainStateProof`], and returns - /// [`RevealedDidMerkleProofLeaves`] if the proof is successfully verified. - /// This information is then made availabe as an origin to the downstream - /// call dispatched. + /// The generic types are the following: /// - /// The verifier performs the following steps: - /// 1. Verifies the state proof about the state root of the relaychain block - /// at the provided height. The state root is retrieved from the provided - /// relaychain header, which is checked to be the header of a - /// previously-finalized relaychain block. - /// 2. Verifies the state proof about the DIP commitment value on the - /// provider parachain at the block finalized at the given relaychain - /// block, using the relay state root validated in the previous step. - /// 3. Verifies the DIP Merkle proof revealing parts of the subject's DID - /// Document against the retrieved DIP commitment validated in the - /// previous step. - /// 4. Verifies the cross-chain DID signature over the payload composed by - /// the SCALE-encoded tuple of `(C, D, S, B, G, E)`, with: - /// * `C`: The `RuntimeCall` to dispatch after performing DIP - /// verification. - /// * `D`: The local details associated to the DID subject as stored in - /// the [`pallet_dip_consumer`] `IdentityEntries` storage map. - /// * `S`: The tx submitter's address. - /// * `B`: The block number of the consumer chain provided in the - /// cross-chain DID signature. - /// * `G`: The genesis hash of the consumer chain. - /// * `E`: Any additional information provided by the - /// `LocalContextProvider` implementation. - /// The generic types - /// indicate the following: - /// * `RelayChainInfo`: The type providing information about the consumer - /// (relay)chain. - /// * `ChildProviderParachainId`: The parachain ID of the provider KILT - /// child parachain. - /// * `ChildProviderStateInfo`: The type providing storage and state - /// information about the provider KILT child parachain. - /// * `ProviderDipMerkleHasher`: The hashing algorithm used by the KILT - /// parachain for the generation of the DIP identity commitment. - /// * `ProviderDidKeyId`: The runtime type of a DID key ID as defined by the - /// KILT child parachain. - /// * `ProviderAccountId`: The runtime type of an account ID as defined by - /// the KILT child parachain. - /// * `ProviderWeb3Name`: The runtime type of a web3name as defined by the - /// KILT child parachain. - /// * `ProviderLinkedAccountId`: The runtime type of a linked account ID as - /// defined by the KILT child parachain. - /// * `MAX_REVEALED_KEYS_COUNT`: Max number of DID keys that the verifier - /// will accept revealed as part of the DIP identity proof. - /// * `MAX_REVEALED_ACCOUNTS_COUNT`: Max number of linked accounts that the - /// verifier will accept revealed as part of the DIP identity proof. - /// * `LocalContextProvider`: The type providing context of the consumer - /// chain (e.g., current block number) for the sake of cross-chain DID - /// signature verification. - /// * `LocalDidCallVerifier`: Logic to map `RuntimeCall`s to a specific DID - /// key relationship. This information is used once the Merkle proof is + /// * `ConsumerBlockHashStore`: A type providing block hashes for the + /// relaychain blocks. + /// * `KILT_PARA_ID`: The ID of the specific KILT parachain instance. + /// * `KiltRuntime`: A KILT runtime definition. + /// * `DidCallVerifier`: Logic to map `RuntimeCall`s to a specific DID key + /// relationship. This information is used once the Merkle proof is /// verified, to filter only the revealed keys that match the provided /// relationship. + /// * `SignedExtra`: Any additional information that must be signed by the + /// DID subject in the cross-chain operation. + /// * `MAX_PROVIDER_HEAD_PROOF_LEAVE_COUNT`: The maximum number of leaves + /// that can be revealed as part of the parachain head storage proof. + /// * `MAX_PROVIDER_HEAD_PROOF_LEAVE_SIZE`: The maximum size of each leaf + /// revealed as part of the parachain head storage proof. + /// * `MAX_DIP_COMMITMENT_PROOF_LEAVE_COUNT`: The maximum number of leaves + /// that can be revealed as part of the DIP commitment storage proof. + /// * `MAX_DIP_COMMITMENT_PROOF_LEAVE_SIZE`: The maximum size of each leaf + /// revealed as part of the DIP commitment storage proof. + /// * `MAX_DID_MERKLE_PROOF_LEAVE_COUNT`: The maximum number of *blinded* + /// leaves that can be revealed as part of the DID Merkle proof. + /// * `MAX_DID_MERKLE_PROOF_LEAVE_SIZE`: The maximum size of each *blinded* + /// leaf revealed as part of the DID Merkle proof. + /// * `MAX_DID_MERKLE_PROOF_LEAVE_SIZE`: The maximum number of leaves that + /// can be revealed as part of the DID Merkle proof. pub struct RelaychainVerifier< - RelayChainInfo, - ChildProviderParachainId, - ChildProviderStateInfo, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, - LocalContextProvider, - LocalDidCallVerifier, + ConsumerBlockHashStore, + const KILT_PARA_ID: u32, + KiltRuntime, + DidCallVerifier, + SignedExtra, + const MAX_PROVIDER_HEAD_PROOF_LEAVE_COUNT: u32, + const MAX_PROVIDER_HEAD_PROOF_LEAVE_SIZE: u32, + const MAX_DIP_COMMITMENT_PROOF_LEAVE_COUNT: u32, + const MAX_DIP_COMMITMENT_PROOF_LEAVE_SIZE: u32, + const MAX_DID_MERKLE_PROOF_LEAVE_COUNT: u32, + const MAX_DID_MERKLE_PROOF_LEAVE_SIZE: u32, + const MAX_DID_MERKLE_LEAVES_REVEALED: u32, >( #[allow(clippy::type_complexity)] - PhantomData<( - RelayChainInfo, - ChildProviderParachainId, - ChildProviderStateInfo, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - LocalContextProvider, - LocalDidCallVerifier, - )>, + PhantomData<(ConsumerBlockHashStore, KiltRuntime, DidCallVerifier, SignedExtra)>, ); impl< ConsumerRuntime, - RelayChainInfo, - ChildProviderParachainId, - ChildProviderStateInfo, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, - LocalContextProvider, - LocalDidCallVerifier, + ConsumerBlockHashStore, + const KILT_PARA_ID: u32, + KiltRuntime, + DidCallVerifier, + SignedExtra, + const MAX_PROVIDER_HEAD_PROOF_LEAVE_COUNT: u32, + const MAX_PROVIDER_HEAD_PROOF_LEAVE_SIZE: u32, + const MAX_DIP_COMMITMENT_PROOF_LEAVE_COUNT: u32, + const MAX_DIP_COMMITMENT_PROOF_LEAVE_SIZE: u32, + const MAX_DID_MERKLE_PROOF_LEAVE_COUNT: u32, + const MAX_DID_MERKLE_PROOF_LEAVE_SIZE: u32, + const MAX_DID_MERKLE_LEAVES_REVEALED: u32, > IdentityProofVerifier for RelaychainVerifier< - RelayChainInfo, - ChildProviderParachainId, - ChildProviderStateInfo, - ProviderDipMerkleHasher, - ProviderDidKeyId, - ProviderAccountId, - ProviderWeb3Name, - ProviderLinkedAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - LocalContextProvider, - LocalDidCallVerifier, + ConsumerBlockHashStore, + KILT_PARA_ID, + KiltRuntime, + DidCallVerifier, + SignedExtra, + MAX_PROVIDER_HEAD_PROOF_LEAVE_COUNT, + MAX_PROVIDER_HEAD_PROOF_LEAVE_SIZE, + MAX_DIP_COMMITMENT_PROOF_LEAVE_COUNT, + MAX_DIP_COMMITMENT_PROOF_LEAVE_SIZE, + MAX_DID_MERKLE_PROOF_LEAVE_COUNT, + MAX_DID_MERKLE_PROOF_LEAVE_SIZE, + MAX_DID_MERKLE_LEAVES_REVEALED, > where - ConsumerRuntime: pallet_dip_consumer::Config, + ConsumerRuntime: pallet_dip_consumer::Config, ConsumerRuntime::LocalIdentityInfo: Incrementable + Default, - - RelayChainInfo: RelayChainStorageInfo> - + HistoricalBlockRegistry< - BlockNumber = ::BlockNumber, - Hasher = ::Hasher, - >, - ::Hasher: Parameter + 'static, - OutputOf<::Hasher>: - Ord + Default + sp_std::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec, - ::BlockNumber: Copy - + Into - + TryFrom - + HasCompact - + Member - + sp_std::hash::Hash - + MaybeDisplay - + AtLeast32BitUnsigned - + Codec - + Parameter - + 'static, - RelayChainInfo::Key: AsRef<[u8]>, - - ChildProviderParachainId: Get, - - ChildProviderStateInfo: ProviderParachainStorageInfo< - Identifier = ConsumerRuntime::Identifier, - Commitment = ProviderDipMerkleHasher::Out, - >, - OutputOf: - Ord + From::Hasher>>, - ChildProviderStateInfo::BlockNumber: Parameter + 'static, - ChildProviderStateInfo::Commitment: Decode, - ChildProviderStateInfo::Key: AsRef<[u8]>, - - LocalContextProvider: - DidSignatureVerifierContext, Hash = ConsumerRuntime::Hash>, - LocalContextProvider::SignedExtra: Encode, - LocalDidCallVerifier: DipCallOriginFilter< + BlockNumberFor: Into + TryFrom, + ConsumerBlockHashStore: + GetWithArg, Result = Option>>, + KiltRuntime: frame_system::Config + + pallet_dip_provider::Config + + did::Config + + pallet_web3_names::Config + + pallet_did_lookup::Config, + KiltRuntime::IdentityCommitmentGenerator: + IdentityCommitmentGenerator, + IdentityCommitmentOf: Into, + SignedExtra: GetWithoutArg, + SignedExtra::Result: Encode, + DidCallVerifier: DipCallOriginFilter< RuntimeCallOf, - OriginInfo = (DidVerificationKey, DidVerificationKeyRelationship), + OriginInfo = RevealedDidKey, BlockNumberFor, KiltRuntime::AccountId>, >, - - ProviderDipMerkleHasher: sp_core::Hasher, - ProviderDidKeyId: Parameter + 'static + Into, - ProviderAccountId: Parameter + 'static, - ProviderLinkedAccountId: Parameter + 'static, - ProviderWeb3Name: Parameter + 'static, + DidCallVerifier::Error: Into, { - type Error = DipRelaychainStateProofVerifierError< - ParachainHeadProofVerifierError, - DipIdentityCommitmentProofVerifierError, - DidMerkleProofVerifierError, - RevealedDidKeysSignatureAndCallVerifierError, - >; - type Proof = RelaychainDipStateProof< - ::BlockNumber, - ::Hasher, - BoundedBlindedValue, - RevealedDidMerkleProofLeaf< - ProviderDidKeyId, - ProviderAccountId, - ChildProviderStateInfo::BlockNumber, - ProviderWeb3Name, - ProviderLinkedAccountId, - >, + type Error = DipRelaychainStateProofVerifierError; + type Proof = RelayDipDidProof< + BlockNumberFor, + ConsumerRuntime::Hashing, + KeyIdOf, + KiltRuntime::AccountId, + BlockNumberFor, + Web3NameOf, + LinkableAccountId, >; - type VerificationResult = RevealedDidMerkleProofLeaves< - ProviderDidKeyId, - ProviderAccountId, - ChildProviderStateInfo::BlockNumber, - ProviderWeb3Name, - ProviderLinkedAccountId, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, + type VerificationResult = DipOriginInfo< + KeyIdOf, + KiltRuntime::AccountId, + BlockNumberFor, + Web3NameOf, + LinkableAccountId, + MAX_DID_MERKLE_LEAVES_REVEALED, >; fn verify_proof_for_call_against_details( @@ -746,60 +340,90 @@ pub mod v0 { identity_details: &mut Option, proof: Self::Proof, ) -> Result { - // 1. Retrieve block hash from provider at the proof height - let block_hash_at_height = RelayChainInfo::block_hash_for(&proof.para_state_root.relay_block_height) - .ok_or(DipRelaychainStateProofVerifierError::InvalidBlockHeight)?; - - // 1.1 Verify that the provided header hashes to the same block has retrieved - if block_hash_at_height != proof.relay_header.hash() { - return Err(DipRelaychainStateProofVerifierError::InvalidBlockHash); - } - // 1.2 If so, extract the state root from the header - let state_root_at_height = proof.relay_header.state_root; - - // 2. Verify relay chain proof - let provider_parachain_header = - ParachainHeadProofVerifier::::verify_proof_for_parachain_with_root( - &ChildProviderParachainId::get(), - &state_root_at_height, - proof.para_state_root.proof, - ) - .map_err(DipRelaychainStateProofVerifierError::ParachainHeadMerkleProof)?; - - // 3. Verify parachain state proof. - let subject_identity_commitment = - DipIdentityCommitmentProofVerifier::::verify_proof_for_identifier( - subject, - provider_parachain_header.state_root.into(), - proof.dip_identity_commitment, - ) - .map_err(DipRelaychainStateProofVerifierError::IdentityCommitmentMerkleProof)?; - - // 4. Verify DIP merkle proof. - let proof_leaves = verify_dip_merkle_proof::< - ProviderDipMerkleHasher, - _, - _, - _, - _, - _, - MAX_REVEALED_KEYS_COUNT, - MAX_REVEALED_ACCOUNTS_COUNT, - >(&subject_identity_commitment, proof.did.leaves) - .map_err(DipRelaychainStateProofVerifierError::DipProof)?; - - // 5. Verify DID signature. - verify_did_signature_for_call::<_, _, _, _, LocalContextProvider, _, _, _, LocalDidCallVerifier>( - call, - submitter, - identity_details, - RevealedDidKeysAndSignature { - merkle_leaves: proof_leaves.borrow(), - did_signature: proof.did.signature, - }, - ) - .map_err(DipRelaychainStateProofVerifierError::DidSignature)?; - Ok(proof_leaves) + // 1. Verify provided relaychain header. + let proof_without_header = proof + .verify_relay_header::() + .map_err(DipRelaychainStateProofVerifierError::ProofVerification)?; + + // 2. Verify parachain state is finalized by relay chain and fresh. + ensure!( + proof_without_header.provider_head_proof.proof.len() + <= MAX_PROVIDER_HEAD_PROOF_LEAVE_COUNT.saturated_into(), + DipRelaychainStateProofVerifierError::ProofComponentTooLarge(0) + ); + ensure!( + proof_without_header + .provider_head_proof + .proof + .iter() + .all(|l| l.len() <= MAX_PROVIDER_HEAD_PROOF_LEAVE_SIZE.saturated_into()), + DipRelaychainStateProofVerifierError::ProofComponentTooLarge(1) + ); + let proof_without_relaychain = proof_without_header + .verify_provider_head_proof::>(KILT_PARA_ID) + .map_err(DipRelaychainStateProofVerifierError::ProofVerification)?; + + // 3. Verify commitment is included in provider parachain state. + ensure!( + proof_without_relaychain.dip_commitment_proof.0.len() + <= MAX_DIP_COMMITMENT_PROOF_LEAVE_COUNT.saturated_into(), + DipRelaychainStateProofVerifierError::ProofComponentTooLarge(2) + ); + ensure!( + proof_without_relaychain + .dip_commitment_proof + .0 + .iter() + .all(|l| l.len() <= MAX_DIP_COMMITMENT_PROOF_LEAVE_SIZE.saturated_into()), + DipRelaychainStateProofVerifierError::ProofComponentTooLarge(3) + ); + let proof_without_parachain = proof_without_relaychain + .verify_dip_commitment_proof_for_subject::(subject) + .map_err(DipRelaychainStateProofVerifierError::ProofVerification)?; + + // 4. Verify DIP Merkle proof. + ensure!( + proof_without_parachain.dip_proof.blinded.len() <= MAX_DID_MERKLE_PROOF_LEAVE_COUNT.saturated_into(), + DipRelaychainStateProofVerifierError::ProofComponentTooLarge(4) + ); + ensure!( + proof_without_parachain + .dip_proof + .blinded + .iter() + .all(|l| l.len() <= MAX_DID_MERKLE_PROOF_LEAVE_SIZE.saturated_into()), + DipRelaychainStateProofVerifierError::ProofComponentTooLarge(5) + ); + let proof_without_dip_merkle = proof_without_parachain + .verify_dip_proof::() + .map_err(DipRelaychainStateProofVerifierError::ProofVerification)?; + + // 5. Verify call is signed by one of the DID keys revealed in the proof + let current_block_number = frame_system::Pallet::::block_number(); + let consumer_genesis_hash = + frame_system::Pallet::::block_hash(BlockNumberFor::::zero()); + let signed_extra = SignedExtra::get(); + let encoded_payload = (call, &identity_details, submitter, consumer_genesis_hash, signed_extra).encode(); + let revealed_did_info = proof_without_dip_merkle + .verify_signature_time(¤t_block_number) + .and_then(|p| p.retrieve_signing_leaf_for_payload(&encoded_payload[..])) + .map_err(DipRelaychainStateProofVerifierError::ProofVerification)?; + + // 6. Verify the signing key fulfills the requirements + let signing_key = revealed_did_info + .get_signing_leaf() + .map_err(DipRelaychainStateProofVerifierError::ProofVerification)?; + DidCallVerifier::check_call_origin_info(call, signing_key) + .map_err(DipRelaychainStateProofVerifierError::DidOriginError)?; + + // 7. Increment the local details + if let Some(details) = identity_details { + details.increment(); + } else { + *identity_details = Some(Default::default()); + }; + + Ok(revealed_did_info) } } } diff --git a/dip-template/nodes/dip-consumer/Cargo.toml b/dip-template/nodes/dip-consumer/Cargo.toml index 76fcdebdc..e00bb993d 100644 --- a/dip-template/nodes/dip-consumer/Cargo.toml +++ b/dip-template/nodes/dip-consumer/Cargo.toml @@ -15,7 +15,6 @@ build = "build.rs" clap = {workspace = true, features = ["std", "derive"]} futures = { workspace = true } log = { workspace = true, features = ["std"] } -parity-scale-codec = { workspace = true, features = ["std"] } serde = {workspace = true, features = ["std", "derive"]} jsonrpsee = {workspace = true, features = ["server"]} diff --git a/dip-template/nodes/dip-provider/Cargo.toml b/dip-template/nodes/dip-provider/Cargo.toml index 6ca76df48..e792a8236 100644 --- a/dip-template/nodes/dip-provider/Cargo.toml +++ b/dip-template/nodes/dip-provider/Cargo.toml @@ -15,7 +15,6 @@ build = "build.rs" clap = {workspace = true, features = ["std", "derive"]} futures = { workspace = true } log = { workspace = true, features = ["std"] } -parity-scale-codec = { workspace = true, features = ["std"] } serde = {workspace = true, features = ["std", "derive"]} jsonrpsee = {workspace = true, features = ["server"]} diff --git a/dip-template/runtimes/dip-consumer/Cargo.toml b/dip-template/runtimes/dip-consumer/Cargo.toml index f6b7d428d..e2f22e4f7 100644 --- a/dip-template/runtimes/dip-consumer/Cargo.toml +++ b/dip-template/runtimes/dip-consumer/Cargo.toml @@ -21,11 +21,9 @@ scale-info = {workspace = true, features = ["derive"]} dip-provider-runtime-template.workspace = true did.workspace = true kilt-dip-primitives.workspace = true -pallet-did-lookup.workspace = true pallet-dip-consumer.workspace = true pallet-postit.workspace = true pallet-relay-store.workspace = true -runtime-common.workspace = true # Substrate frame-executive.workspace = true @@ -41,7 +39,6 @@ pallet-timestamp.workspace = true pallet-transaction-payment.workspace = true pallet-transaction-payment-rpc-runtime-api.workspace = true pallet-utility.workspace = true -polkadot-parachain.workspace = true sp-api.workspace = true sp-block-builder.workspace = true sp-consensus-aura.workspace = true @@ -57,18 +54,18 @@ sp-weights.workspace = true # Cumulus cumulus-pallet-aura-ext.workspace = true -cumulus-pallet-dmp-queue.workspace = true cumulus-pallet-parachain-system.workspace = true cumulus-primitives-core.workspace = true cumulus-primitives-timestamp.workspace = true -cumulus-primitives-utility.workspace = true pallet-collator-selection.workspace = true parachain-info.workspace = true +# Polkadot +rococo-runtime.workspace = true + # Benchmarks frame-benchmarking = {workspace = true, optional = true} frame-system-benchmarking = {workspace = true, optional = true} -hex-literal = {workspace = true, optional = true} [features] default = [ @@ -80,11 +77,9 @@ std = [ "dip-provider-runtime-template/std", "did/std", "kilt-dip-primitives/std", - "pallet-did-lookup/std", "pallet-dip-consumer/std", "pallet-postit/std", "pallet-relay-store/std", - "runtime-common/std", "frame-executive/std", "frame-support/std", "frame-system/std", @@ -98,7 +93,6 @@ std = [ "pallet-transaction-payment/std", "pallet-transaction-payment-rpc-runtime-api/std", "pallet-utility/std", - "polkadot-parachain/std", "sp-api/std", "sp-block-builder/std", "sp-consensus-aura/std", @@ -112,21 +106,22 @@ std = [ "sp-version/std", "sp-weights/std", "cumulus-pallet-aura-ext/std", - "cumulus-pallet-dmp-queue/std", "cumulus-pallet-parachain-system/std", "cumulus-primitives-core/std", "cumulus-primitives-timestamp/std", - "cumulus-primitives-utility/std", "pallet-collator-selection/std", - "parachain-info/std" + "parachain-info/std", + "rococo-runtime/std", + "frame-benchmarking/std", + "frame-system-benchmarking?/std", ] runtime-benchmarks = [ "dip-provider-runtime-template/runtime-benchmarks", + "did/runtime-benchmarks", "kilt-dip-primitives/runtime-benchmarks", "pallet-dip-consumer/runtime-benchmarks", "pallet-relay-store/runtime-benchmarks", - "runtime-common/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-balances/runtime-benchmarks", @@ -135,7 +130,7 @@ runtime-benchmarks = [ "pallet-timestamp/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "rococo-runtime/runtime-benchmarks", "frame-benchmarking/runtime-benchmarks", "frame-system-benchmarking/runtime-benchmarks", - "hex-literal" ] diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index deeea55b5..1e6ec1dae 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -16,31 +16,32 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -use did::{did_details::DidVerificationKey, DidVerificationKeyRelationship}; +use did::{DidVerificationKeyRelationship, KeyIdOf}; use dip_provider_runtime_template::{AccountId as ProviderAccountId, Runtime as ProviderRuntime}; use frame_support::traits::Contains; -use frame_system::EnsureSigned; +use frame_system::{pallet_prelude::BlockNumberFor, EnsureSigned}; use kilt_dip_primitives::{ - traits::DipCallOriginFilter, KiltVersionedParachainVerifier, RelayStateRootsViaRelayStorePallet, + traits::DipCallOriginFilter, KiltVersionedParachainVerifier, RelayStateRootsViaRelayStorePallet, RevealedDidKey, }; use pallet_dip_consumer::traits::IdentityProofVerifier; +use rococo_runtime::Runtime as RelaychainRuntime; use sp_core::ConstU32; -use sp_runtime::traits::BlakeTwo256; +use sp_std::marker::PhantomData; use crate::{weights, AccountId, DidIdentifier, Runtime, RuntimeCall, RuntimeOrigin}; pub type MerkleProofVerifierOutput = >::VerificationResult; -/// The verifier logic assumes the provider is a sibling KILT parachain, and +/// The verifier logic assumes the provider is a sibling KILT parachain, the relaychain is a Rococo relaychain, and /// that a KILT subject can provide DIP proof that reveal at most 10 DID keys /// and 10 linked accounts (defaults provided by the /// `KiltVersionedParachainVerifier` type). Calls that do not pass the /// [`DipCallFilter`] will be discarded early on in the verification process. pub type ProofVerifier = KiltVersionedParachainVerifier< - ProviderRuntime, - ConstU32<2_000>, + RelaychainRuntime, RelayStateRootsViaRelayStorePallet, - BlakeTwo256, - DipCallFilter, + 2_000, + ProviderRuntime, + DipCallFilter, BlockNumberFor, ProviderAccountId>, >; impl pallet_dip_consumer::Config for Runtime { @@ -88,6 +89,8 @@ impl Contains for PreliminaryDipOriginFilter { fn derive_verification_key_relationship(call: &RuntimeCall) -> Option { match call { RuntimeCall::PostIt { .. } => Some(DidVerificationKeyRelationship::Authentication), + #[cfg(feature = "runtime-benchmarks")] + RuntimeCall::System(frame_system::Call::remark { .. }) => Some(DidVerificationKeyRelationship::Authentication), RuntimeCall::Utility(pallet_utility::Call::batch { calls }) => single_key_relationship(calls.iter()).ok(), RuntimeCall::Utility(pallet_utility::Call::batch_all { calls }) => single_key_relationship(calls.iter()).ok(), RuntimeCall::Utility(pallet_utility::Call::force_batch { calls }) => single_key_relationship(calls.iter()).ok(), @@ -126,21 +129,42 @@ pub enum DipCallFilterError { WrongVerificationRelationship, } +impl From for u8 { + fn from(value: DipCallFilterError) -> Self { + match value { + // DO NOT USE 0 + // Errors of different sub-parts are separated by a `u8::MAX`. + // A value of 0 would make it confusing whether it's the previous sub-part error (u8::MAX) + // or the new sub-part error (u8::MAX + 0). + DipCallFilterError::BadOrigin => 1, + DipCallFilterError::WrongVerificationRelationship => 2, + } + } +} + /// A call filter that requires calls to the [`pallet_postit::Pallet`] pallet to /// be authorized with a DID signature generated with a key of a given /// verification relationship. -pub struct DipCallFilter; +pub struct DipCallFilter( + PhantomData<(ProviderDidKeyId, ProviderBlockNumber, ProviderAccountId)>, +); -impl DipCallOriginFilter for DipCallFilter { +impl DipCallOriginFilter + for DipCallFilter +{ type Error = DipCallFilterError; - type OriginInfo = (DidVerificationKey, DidVerificationKeyRelationship); + type OriginInfo = RevealedDidKey; type Success = (); // Accepts only a DipOrigin for the DidLookup pallet calls. fn check_call_origin_info(call: &RuntimeCall, info: &Self::OriginInfo) -> Result { - let key_relationship = + let revealed_key_relationship: DidVerificationKeyRelationship = info + .relationship + .try_into() + .map_err(|_| DipCallFilterError::WrongVerificationRelationship)?; + let expected_key_relationship = single_key_relationship([call].into_iter()).map_err(|_| DipCallFilterError::BadOrigin)?; - if info.1 == key_relationship { + if revealed_key_relationship == expected_key_relationship { Ok(()) } else { Err(DipCallFilterError::WrongVerificationRelationship) diff --git a/dip-template/runtimes/dip-consumer/src/origin_adapter.rs b/dip-template/runtimes/dip-consumer/src/origin_adapter.rs index 660dc7778..346848e1a 100644 --- a/dip-template/runtimes/dip-consumer/src/origin_adapter.rs +++ b/dip-template/runtimes/dip-consumer/src/origin_adapter.rs @@ -18,6 +18,7 @@ use crate::{AccountId, DidIdentifier, MerkleProofVerifierOutput, RuntimeOrigin, Web3Name}; use frame_support::traits::EnsureOrigin; +use kilt_dip_primitives::RevealedDidMerkleProofLeaf; use pallet_dip_consumer::{DipOrigin, EnsureDipOrigin}; use pallet_postit::traits::GetUsername; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; @@ -52,12 +53,18 @@ pub struct DipOriginAdapter(DipOrigin Result { self.0 .details - .web3_name - .as_ref() - .map(|leaf| leaf.web3_name.clone()) + .iter_leaves() + .find_map(|revealed_leaf| { + if let RevealedDidMerkleProofLeaf::Web3Name(revealed_web3name_leaf) = revealed_leaf { + Some(revealed_web3name_leaf.web3_name.clone()) + } else { + None + } + }) .ok_or("No username for the subject.") } } diff --git a/dip-template/runtimes/dip-provider/Cargo.toml b/dip-template/runtimes/dip-provider/Cargo.toml index 63b4302ff..94a736ad7 100644 --- a/dip-template/runtimes/dip-provider/Cargo.toml +++ b/dip-template/runtimes/dip-provider/Cargo.toml @@ -20,8 +20,6 @@ scale-info = {workspace = true, features = ["derive"]} # DIP did.workspace = true -kilt-support.workspace = true -kilt-dip-primitives.workspace = true kilt-runtime-api-did.workspace = true kilt-runtime-api-dip-provider.workspace = true pallet-deposit-storage.workspace = true @@ -59,18 +57,15 @@ sp-weights.workspace = true # Cumulus cumulus-pallet-aura-ext.workspace = true -cumulus-pallet-dmp-queue.workspace = true cumulus-pallet-parachain-system.workspace = true cumulus-primitives-core.workspace = true cumulus-primitives-timestamp.workspace = true -cumulus-primitives-utility.workspace = true pallet-collator-selection.workspace = true parachain-info.workspace = true # Benchmarks frame-benchmarking = {workspace = true, optional = true} frame-system-benchmarking = {workspace = true, optional = true} -hex-literal = {workspace = true, optional = true} [features] default = [ @@ -81,8 +76,6 @@ std = [ "parity-scale-codec/std", "scale-info/std", "did/std", - "kilt-support/std", - "kilt-dip-primitives/std", "kilt-runtime-api-did/std", "kilt-runtime-api-dip-provider/std", "pallet-deposit-storage/std", @@ -116,18 +109,16 @@ std = [ "sp-version/std", "sp-weights/std", "cumulus-pallet-aura-ext/std", - "cumulus-pallet-dmp-queue/std", "cumulus-pallet-parachain-system/std", "cumulus-primitives-core/std", "cumulus-primitives-timestamp/std", - "cumulus-primitives-utility/std", "pallet-collator-selection/std", "parachain-info/std", "frame-benchmarking?/std", + "frame-system-benchmarking?/std", ] runtime-benchmarks = [ "did/runtime-benchmarks", - "kilt-dip-primitives/runtime-benchmarks", "pallet-deposit-storage/runtime-benchmarks", "pallet-did-lookup/runtime-benchmarks", "pallet-dip-provider/runtime-benchmarks", @@ -143,5 +134,4 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "frame-benchmarking/runtime-benchmarks", "frame-system-benchmarking/runtime-benchmarks", - "hex-literal" ] diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index 6ecfe2df7..448094067 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -127,7 +127,11 @@ pub mod deposit { impl From for u16 { fn from(value: CommitmentDepositRemovalHookError) -> Self { match value { - CommitmentDepositRemovalHookError::DecodeKey => 0, + // DO NOT USE 0 + // Errors of different sub-parts are separated by a `u8::MAX`. + // A value of 0 would make it confusing whether it's the previous sub-part error (u8::MAX) + // or the new sub-part error (u8::MAX + 0). + CommitmentDepositRemovalHookError::DecodeKey => 1, CommitmentDepositRemovalHookError::Internal => u16::MAX, } } diff --git a/nodes/parachain/src/chain_spec/clone.rs b/nodes/parachain/src/chain_spec/clone.rs index 099a75f36..f002be4a5 100644 --- a/nodes/parachain/src/chain_spec/clone.rs +++ b/nodes/parachain/src/chain_spec/clone.rs @@ -22,14 +22,12 @@ use cumulus_primitives_core::ParaId; use hex_literal::hex; use runtime_common::constants::KILT; use sc_chain_spec::ChainType; -use sp_core::crypto::UncheckedInto; -use sp_core::sr25519; +use sp_core::{crypto::UncheckedInto, sr25519}; use clone_runtime::{ - BalancesConfig, ParachainInfoConfig, PolkadotXcmConfig, RuntimeGenesisConfig, SessionConfig, SudoConfig, - SystemConfig, + BalancesConfig, CollatorSelectionConfig, ParachainInfoConfig, PolkadotXcmConfig, RuntimeGenesisConfig, + SessionConfig, SudoConfig, SystemConfig, WASM_BINARY, }; -use clone_runtime::{CollatorSelectionConfig, WASM_BINARY}; use runtime_common::{AccountId, AuthorityId, Balance}; use super::{get_account_id_from_seed, get_from_seed, get_properties, DEFAULT_PARA_ID}; diff --git a/pallets/did/src/did_details.rs b/pallets/did/src/did_details.rs index 86f7150b0..d28291f96 100644 --- a/pallets/did/src/did_details.rs +++ b/pallets/did/src/did_details.rs @@ -39,7 +39,7 @@ use crate::{ }; /// Public verification key that a DID can control. -#[derive(Clone, Decode, RuntimeDebug, Encode, Eq, Ord, PartialEq, PartialOrd, TypeInfo, MaxEncodedLen)] +#[derive(Clone, Copy, Decode, RuntimeDebug, Encode, Eq, Ord, PartialEq, PartialOrd, TypeInfo, MaxEncodedLen)] pub enum DidVerificationKey { /// An Ed25519 public key. Ed25519(ed25519::Public), @@ -118,7 +118,7 @@ pub enum DidEncryptionKey { } /// A general public key under the control of the DID. -#[derive(Clone, Decode, RuntimeDebug, Encode, Eq, Ord, PartialEq, PartialOrd, TypeInfo, MaxEncodedLen)] +#[derive(Clone, Copy, Decode, RuntimeDebug, Encode, Eq, Ord, PartialEq, PartialOrd, TypeInfo, MaxEncodedLen)] pub enum DidPublicKey { /// A verification key, used to generate and verify signatures. PublicVerificationKey(DidVerificationKey), @@ -153,7 +153,7 @@ pub enum DidVerificationKeyRelationship { } /// Types of signatures supported by this pallet. -#[derive(Clone, Decode, RuntimeDebug, Encode, Eq, PartialEq, TypeInfo)] +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo)] pub enum DidSignature { /// A Ed25519 signature. Ed25519(ed25519::Signature), @@ -264,7 +264,7 @@ impl, AccountId> 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)] +#[derive(Clone, Copy, RuntimeDebug, Decode, Encode, PartialEq, Ord, PartialOrd, Eq, TypeInfo, MaxEncodedLen)] pub struct DidPublicKeyDetails { /// A public key the DID controls. pub key: DidPublicKey, diff --git a/pallets/pallet-deposit-storage/src/deposit.rs b/pallets/pallet-deposit-storage/src/deposit.rs index d5852a662..53f1a2585 100644 --- a/pallets/pallet-deposit-storage/src/deposit.rs +++ b/pallets/pallet-deposit-storage/src/deposit.rs @@ -59,10 +59,14 @@ pub enum FixedDepositCollectorViaDepositsPalletError { impl From for u16 { fn from(value: FixedDepositCollectorViaDepositsPalletError) -> Self { match value { - FixedDepositCollectorViaDepositsPalletError::DepositAlreadyTaken => 0, - FixedDepositCollectorViaDepositsPalletError::DepositNotFound => 1, - FixedDepositCollectorViaDepositsPalletError::FailedToHold => 2, - FixedDepositCollectorViaDepositsPalletError::FailedToRelease => 3, + // DO NOT USE 0 + // Errors of different sub-parts are separated by a `u8::MAX`. + // A value of 0 would make it confusing whether it's the previous sub-part error (u8::MAX) + // or the new sub-part error (u8::MAX + 0). + FixedDepositCollectorViaDepositsPalletError::DepositAlreadyTaken => 1, + FixedDepositCollectorViaDepositsPalletError::DepositNotFound => 2, + FixedDepositCollectorViaDepositsPalletError::FailedToHold => 3, + FixedDepositCollectorViaDepositsPalletError::FailedToRelease => 4, FixedDepositCollectorViaDepositsPalletError::Internal => u16::MAX, } } diff --git a/pallets/pallet-dip-consumer/Cargo.toml b/pallets/pallet-dip-consumer/Cargo.toml index ac4758d3f..edfd082d6 100644 --- a/pallets/pallet-dip-consumer/Cargo.toml +++ b/pallets/pallet-dip-consumer/Cargo.toml @@ -25,12 +25,10 @@ frame-system.workspace = true kilt-support.workspace = true parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} -sp-core.workspace = true sp-std.workspace = true # Benchmarks frame-benchmarking = {workspace = true, optional = true} -sp-runtime = {workspace = true, optional = true} [features] default = [ "std" ] @@ -48,10 +46,8 @@ std = [ "kilt-support/std", "parity-scale-codec/std", "scale-info/std", - "sp-core/std", "sp-std/std", "frame-benchmarking?/std", - "sp-runtime?/std", ] -try-runtime = [] +try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/pallet-dip-provider/Cargo.toml b/pallets/pallet-dip-provider/Cargo.toml index 6e284e0d1..e6784055b 100644 --- a/pallets/pallet-dip-provider/Cargo.toml +++ b/pallets/pallet-dip-provider/Cargo.toml @@ -26,7 +26,6 @@ frame-system.workspace = true kilt-support.workspace = true parity-scale-codec = {workspace = true, features = ["derive"]} scale-info = {workspace = true, features = ["derive"]} -sp-core.workspace = true sp-std.workspace = true # Benchmarks @@ -51,10 +50,9 @@ std = [ "kilt-support/std", "parity-scale-codec/std", "scale-info/std", - "sp-core/std", "sp-std/std", "frame-benchmarking?/std", "sp-runtime?/std", ] -try-runtime = [ "did/try-runtime", "kilt-support/try-runtime" ] +try-runtime = [ "did/try-runtime", "frame-support/try-runtime", "kilt-support/try-runtime" ] diff --git a/runtimes/clone/src/lib.rs b/runtimes/clone/src/lib.rs index b4da182d5..cd89eeecb 100644 --- a/runtimes/clone/src/lib.rs +++ b/runtimes/clone/src/lib.rs @@ -26,11 +26,11 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; -use frame_support::PalletId; use frame_support::{ construct_runtime, parameter_types, traits::Everything, weights::{ConstantMultiplier, Weight}, + PalletId, }; use frame_system::EnsureRoot; use pallet_transaction_payment::CurrencyAdapter; diff --git a/runtimes/common/src/dip/did.rs b/runtimes/common/src/dip/did.rs index 6986193de..212166cdc 100644 --- a/runtimes/common/src/dip/did.rs +++ b/runtimes/common/src/dip/did.rs @@ -19,7 +19,7 @@ use did::did_details::DidDetails; use frame_support::ensure; use frame_system::pallet_prelude::BlockNumberFor; -use kilt_dip_primitives::merkle::RevealedWeb3Name; +use kilt_dip_primitives::RevealedWeb3Name; use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_dip_provider::traits::IdentityProvider; use parity_scale_codec::{Decode, Encode}; @@ -42,9 +42,13 @@ pub enum LinkedDidInfoProviderError { impl From for u16 { fn from(value: LinkedDidInfoProviderError) -> Self { match value { - LinkedDidInfoProviderError::DidNotFound => 0, - LinkedDidInfoProviderError::DidDeleted => 1, - LinkedDidInfoProviderError::TooManyLinkedAccounts => 2, + // DO NOT USE 0 + // Errors of different sub-parts are separated by a `u8::MAX`. + // A value of 0 would make it confusing whether it's the previous sub-part error (u8::MAX) + // or the new sub-part error (u8::MAX + 0). + LinkedDidInfoProviderError::DidNotFound => 1, + LinkedDidInfoProviderError::DidDeleted => 2, + LinkedDidInfoProviderError::TooManyLinkedAccounts => 3, LinkedDidInfoProviderError::Internal => u16::MAX, } } diff --git a/runtimes/common/src/dip/merkle.rs b/runtimes/common/src/dip/merkle.rs index 693c26e26..73f92e83d 100644 --- a/runtimes/common/src/dip/merkle.rs +++ b/runtimes/common/src/dip/merkle.rs @@ -18,7 +18,7 @@ use did::{DidVerificationKeyRelationship, KeyIdOf}; use frame_support::RuntimeDebug; use frame_system::pallet_prelude::BlockNumberFor; -use kilt_dip_primitives::merkle::{DidKeyMerkleKey, DidKeyMerkleValue, DidMerkleProof}; +use kilt_dip_primitives::DidMerkleProof; use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_dip_provider::{ traits::{IdentityCommitmentGenerator, IdentityProvider}, @@ -29,22 +29,16 @@ use scale_info::TypeInfo; use sp_std::{borrow::ToOwned, marker::PhantomData, vec::Vec}; use sp_trie::{generate_trie_proof, LayoutV1, MemoryDB, TrieDBMutBuilder, TrieHash, TrieMut}; -use kilt_dip_primitives::merkle::{DidKeyRelationship, RevealedDidMerkleProofLeaf}; - use crate::dip::did::LinkedDidInfoOf; -pub type BlindedValue = Vec; /// Type of the Merkle proof revealing parts of the DIP identity of a given DID /// subject. pub type DidMerkleProofOf = DidMerkleProof< - Vec, - RevealedDidMerkleProofLeaf< - KeyIdOf, - ::AccountId, - BlockNumberFor, - ::Web3Name, - LinkableAccountId, - >, + KeyIdOf, + ::AccountId, + BlockNumberFor, + ::Web3Name, + LinkableAccountId, >; /// Type of a complete DIP Merkle proof. @@ -63,32 +57,155 @@ pub enum DidMerkleProofError { KeyNotFound, LinkedAccountNotFound, Web3NameNotFound, + TooManyLeaves, Internal, } impl From for u16 { fn from(value: DidMerkleProofError) -> Self { match value { - DidMerkleProofError::UnsupportedVersion => 0, - DidMerkleProofError::KeyNotFound => 1, - DidMerkleProofError::LinkedAccountNotFound => 2, - DidMerkleProofError::Web3NameNotFound => 3, + // DO NOT USE 0 + // Errors of different sub-parts are separated by a `u8::MAX`. + // A value of 0 would make it confusing whether it's the previous sub-part error (u8::MAX) + // or the new sub-part error (u8::MAX + 0). + DidMerkleProofError::UnsupportedVersion => 1, + DidMerkleProofError::KeyNotFound => 2, + DidMerkleProofError::LinkedAccountNotFound => 3, + DidMerkleProofError::Web3NameNotFound => 4, + DidMerkleProofError::TooManyLeaves => 5, DidMerkleProofError::Internal => u16::MAX, } } } pub mod v0 { + use did::did_details::DidDetails; + use kilt_dip_primitives::{ + DidKeyRelationship, RevealedAccountId, RevealedDidKey, RevealedDidMerkleProofLeaf, RevealedWeb3Name, + }; + use pallet_web3_names::Web3NameOf; + use sp_std::vec; + + use crate::dip::did::Web3OwnershipOf; + use super::*; - /// Type of a Merkle leaf revealed as part of a DIP Merkle proof. - type ProofLeafOf = RevealedDidMerkleProofLeaf< - KeyIdOf, - ::AccountId, - BlockNumberFor, - ::Web3Name, - LinkableAccountId, - >; + fn get_auth_leaves( + did_details: &DidDetails, + ) -> Result< + impl Iterator, BlockNumberFor, Runtime::AccountId>>, + DidMerkleProofError, + > + where + Runtime: did::Config, + { + let auth_key_details = did_details + .public_keys + .get(&did_details.authentication_key) + .ok_or_else(|| { + log::error!("Authentication key should be part of the public keys."); + DidMerkleProofError::Internal + })?; + Ok([RevealedDidKey { + id: did_details.authentication_key, + relationship: DidVerificationKeyRelationship::Authentication.into(), + details: auth_key_details.clone(), + }] + .into_iter()) + } + + fn get_att_leaves( + did_details: &DidDetails, + ) -> Result< + impl Iterator, BlockNumberFor, Runtime::AccountId>>, + DidMerkleProofError, + > + where + Runtime: did::Config, + { + let Some(att_key_id) = did_details.attestation_key else { + return Ok(vec![].into_iter()); + }; + let att_key_details = did_details.public_keys.get(&att_key_id).ok_or_else(|| { + log::error!("Attestation key should be part of the public keys."); + DidMerkleProofError::Internal + })?; + Ok(vec![RevealedDidKey { + id: att_key_id, + relationship: DidVerificationKeyRelationship::AssertionMethod.into(), + details: att_key_details.clone(), + }] + .into_iter()) + } + + fn get_del_leaves( + did_details: &DidDetails, + ) -> Result< + impl Iterator, BlockNumberFor, Runtime::AccountId>>, + DidMerkleProofError, + > + where + Runtime: did::Config, + { + let Some(del_key_id) = did_details.delegation_key else { + return Ok(vec![].into_iter()); + }; + let del_key_details = did_details.public_keys.get(&del_key_id).ok_or_else(|| { + log::error!("Delegation key should be part of the public keys."); + DidMerkleProofError::Internal + })?; + Ok(vec![RevealedDidKey { + id: del_key_id, + relationship: DidVerificationKeyRelationship::CapabilityDelegation.into(), + details: del_key_details.clone(), + }] + .into_iter()) + } + + fn get_enc_leaves( + did_details: &DidDetails, + ) -> Result< + impl Iterator, BlockNumberFor, Runtime::AccountId>>, + DidMerkleProofError, + > + where + Runtime: did::Config, + { + let keys = did_details + .key_agreement_keys + .iter() + .map(|id| { + let key_agreement_details = did_details.public_keys.get(id).ok_or_else(|| { + log::error!("Key agreement key should be part of the public keys."); + DidMerkleProofError::Internal + })?; + Ok(RevealedDidKey { + id: *id, + relationship: DidKeyRelationship::Encryption, + details: key_agreement_details.clone(), + }) + }) + .collect::, _>>()?; + Ok(keys.into_iter()) + } + + fn get_linked_account_leaves( + linked_accounts: &[LinkableAccountId], + ) -> impl Iterator> + '_ { + linked_accounts.iter().cloned().map(RevealedAccountId) + } + + fn get_web3name_leaf( + web3name_details: &Web3OwnershipOf, + ) -> RevealedWeb3Name, BlockNumberFor> + where + Runtime: pallet_web3_names::Config, + { + RevealedWeb3Name { + web3_name: web3name_details.web3_name.clone(), + claimed_at: web3name_details.claimed_at, + } + } /// Given the provided DID info, it calculates the Merkle commitment (root) /// using the provided in-memory DB. @@ -107,135 +224,41 @@ pub mod v0 { let mut trie = TrieHash::>::default(); let mut trie_builder = TrieDBMutBuilder::>::new(db, &mut trie).build(); - // Authentication key - let auth_key_details = did_details - .public_keys - .get(&did_details.authentication_key) - .ok_or_else(|| { - log::error!("Authentication key should be part of the public keys."); - DidMerkleProofError::Internal - })?; - let auth_leaf = ProofLeafOf::::DidKey( - DidKeyMerkleKey( - did_details.authentication_key, - DidVerificationKeyRelationship::Authentication.into(), - ), - DidKeyMerkleValue(auth_key_details.clone()), - ); - trie_builder - .insert(auth_leaf.encoded_key().as_slice(), auth_leaf.encoded_value().as_slice()) - .map_err(|_| { - log::error!( - "Failed to insert authentication key in the trie builder. Authentication leaf: {:#?}", - auth_leaf - ); - DidMerkleProofError::Internal - })?; - // Attestation key, if present - if let Some(att_key_id) = did_details.attestation_key { - let att_key_details = did_details.public_keys.get(&att_key_id).ok_or_else(|| { - log::error!("Attestation key should be part of the public keys."); - DidMerkleProofError::Internal - })?; - let att_leaf = ProofLeafOf::::DidKey( - (att_key_id, DidVerificationKeyRelationship::AssertionMethod.into()).into(), - att_key_details.clone().into(), - ); - trie_builder - .insert(att_leaf.encoded_key().as_slice(), att_leaf.encoded_value().as_slice()) - .map_err(|_| { - log::error!( - "Failed to insert attestation key in the trie builder. Attestation leaf: {:#?}", - att_leaf - ); - DidMerkleProofError::Internal - })?; - }; - // Delegation key, if present - if let Some(del_key_id) = did_details.delegation_key { - let del_key_details = did_details.public_keys.get(&del_key_id).ok_or_else(|| { - log::error!("Delegation key should be part of the public keys."); - DidMerkleProofError::Internal - })?; - let del_leaf = ProofLeafOf::::DidKey( - (del_key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()).into(), - del_key_details.clone().into(), - ); - trie_builder - .insert(del_leaf.encoded_key().as_slice(), del_leaf.encoded_value().as_slice()) - .map_err(|_| { - log::error!( - "Failed to insert delegation key in the trie builder. Delegation leaf: {:#?}", - del_leaf - ); - DidMerkleProofError::Internal - })?; - }; - // Key agreement keys - did_details - .key_agreement_keys - .iter() - .try_for_each(|id| -> Result<(), DidMerkleProofError> { - let key_agreement_details = did_details.public_keys.get(id).ok_or_else(|| { - log::error!("Key agreement key should be part of the public keys."); - DidMerkleProofError::Internal - })?; - let enc_leaf = ProofLeafOf::::DidKey( - (*id, DidKeyRelationship::Encryption).into(), - key_agreement_details.clone().into(), - ); - trie_builder - .insert(enc_leaf.encoded_key().as_slice(), enc_leaf.encoded_value().as_slice()) - .map_err(|_| { - log::error!( - "Failed to insert key agreement key in the trie builder. Key agreement leaf: {:#?}", - enc_leaf - ); - DidMerkleProofError::Internal - })?; - Ok(()) - })?; + // Authentication key. + let auth_leaves = get_auth_leaves(did_details)?; + // Attestation key, if present. + let att_leaves = get_att_leaves(did_details)?; + // Delegation key, if present. + let del_leaves = get_del_leaves(did_details)?; + // Key agreement keys. + let enc_leaves = get_enc_leaves(did_details)?; + // Linked accounts. + let linked_accounts = get_linked_account_leaves(linked_accounts); + // Web3name. + let web3_name = web3_name_details.as_ref().map(get_web3name_leaf::); - // Linked accounts - linked_accounts - .iter() - .try_for_each(|linked_account| -> Result<(), DidMerkleProofError> { - let linked_account_leaf = - ProofLeafOf::::LinkedAccount(linked_account.clone().into(), ().into()); - trie_builder - .insert( - linked_account_leaf.encoded_key().as_slice(), - linked_account_leaf.encoded_value().as_slice(), - ) - .map_err(|_| { - log::error!( - "Failed to insert linked account in the trie builder. Linked account leaf: {:#?}", - linked_account_leaf - ); - DidMerkleProofError::Internal - })?; - Ok(()) - })?; + // Add all leaves to the proof builder. + let keys = auth_leaves + .chain(att_leaves) + .chain(del_leaves) + .chain(enc_leaves) + .map(RevealedDidMerkleProofLeaf::from); + let linked_accounts = linked_accounts.map(RevealedDidMerkleProofLeaf::from); + let web3_names = web3_name + .map(|n| vec![n]) + .unwrap_or_default() + .into_iter() + .map(RevealedDidMerkleProofLeaf::from); - // Web3name, if present - if let Some(web3name_details) = web3_name_details { - let web3_name_leaf = ProofLeafOf::::Web3Name( - web3name_details.web3_name.clone().into(), - web3name_details.claimed_at.into(), - ); + keys.chain(linked_accounts).chain(web3_names).try_for_each(|leaf| { trie_builder - .insert( - web3_name_leaf.encoded_key().as_slice(), - web3_name_leaf.encoded_value().as_slice(), - ) + .insert(leaf.encoded_key().as_slice(), leaf.encoded_value().as_slice()) .map_err(|_| { - log::error!( - "Failed to insert web3name in the trie builder. Web3name leaf: {:#?}", - web3_name_leaf - ); + log::error!("Failed to insert leaf in the trie builder. Leaf: {:#?}", leaf); DidMerkleProofError::Internal })?; - } + Ok(()) + })?; trie_builder.commit(); Ok(trie_builder.root().to_owned()) @@ -274,30 +297,27 @@ pub mod v0 { .ok_or(DidMerkleProofError::KeyNotFound)?; // Create the merkle leaf key depending on the relationship of the key to the // DID document. - let did_key_merkle_key: DidKeyMerkleKey> = if *key_id == did_details.authentication_key - { - Ok((*key_id, DidVerificationKeyRelationship::Authentication.into()).into()) + let key_relationship = if *key_id == did_details.authentication_key { + Ok(DidVerificationKeyRelationship::Authentication.into()) } else if Some(*key_id) == did_details.attestation_key { - Ok((*key_id, DidVerificationKeyRelationship::AssertionMethod.into()).into()) + Ok(DidVerificationKeyRelationship::AssertionMethod.into()) } else if Some(*key_id) == did_details.delegation_key { - Ok((*key_id, DidVerificationKeyRelationship::CapabilityDelegation.into()).into()) + Ok(DidVerificationKeyRelationship::CapabilityDelegation.into()) } else if did_details.key_agreement_keys.contains(key_id) { - Ok((*key_id, DidKeyRelationship::Encryption).into()) + Ok(DidKeyRelationship::Encryption) } else { log::error!("Unknown key ID {:#?} retrieved from DID details.", key_id); Err(DidMerkleProofError::Internal) }?; - Ok(RevealedDidMerkleProofLeaf::DidKey( - did_key_merkle_key, - key_details.clone().into(), - )) + Ok(RevealedDidMerkleProofLeaf::from(RevealedDidKey { + id: *key_id, + relationship: key_relationship, + details: key_details.clone(), + })) }) .chain(account_ids.map(|account_id| -> Result<_, DidMerkleProofError> { if linked_accounts.contains(account_id) { - Ok(RevealedDidMerkleProofLeaf::LinkedAccount( - account_id.clone().into(), - ().into(), - )) + Ok(RevealedDidMerkleProofLeaf::from(RevealedAccountId(account_id.clone()))) } else { Err(DidMerkleProofError::LinkedAccountNotFound) } @@ -307,10 +327,10 @@ pub mod v0 { match (should_include_web3_name, web3_name_details) { // If web3name should be included and it exists, add to the leaves to be revealed... (true, Some(web3name_details)) => { - leaves.push(RevealedDidMerkleProofLeaf::Web3Name( - web3name_details.web3_name.clone().into(), - web3name_details.claimed_at.into(), - )); + leaves.push(RevealedDidMerkleProofLeaf::from(RevealedWeb3Name { + web3_name: web3name_details.web3_name.clone(), + claimed_at: web3name_details.claimed_at, + })); } // ...else if web3name should be included and it DOES NOT exist, return an error... (true, None) => return Err(DidMerkleProofError::Web3NameNotFound), @@ -327,12 +347,10 @@ pub mod v0 { ); DidMerkleProofError::Internal })?; + Ok(CompleteMerkleProof { root, - proof: DidMerkleProofOf:: { - blinded: proof, - revealed: leaves.into_iter().collect(), - }, + proof: DidMerkleProofOf::::new(proof.into_iter().into(), leaves), }) } diff --git a/runtimes/peregrine/src/dip/deposit.rs b/runtimes/peregrine/src/dip/deposit.rs index 065e6d96e..4cec3f934 100644 --- a/runtimes/peregrine/src/dip/deposit.rs +++ b/runtimes/peregrine/src/dip/deposit.rs @@ -74,7 +74,11 @@ pub enum CommitmentDepositRemovalHookError { impl From for u16 { fn from(value: CommitmentDepositRemovalHookError) -> Self { match value { - CommitmentDepositRemovalHookError::DecodeKey => 0, + // DO NOT USE 0 + // Errors of different sub-parts are separated by a `u8::MAX`. + // A value of 0 would make it confusing whether it's the previous sub-part error (u8::MAX) + // or the new sub-part error (u8::MAX + 0). + CommitmentDepositRemovalHookError::DecodeKey => 1, CommitmentDepositRemovalHookError::Internal => u16::MAX, } }