diff --git a/Cargo.lock b/Cargo.lock index 0d0eeaafa8..0c6b1e9f21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2445,7 +2445,7 @@ dependencies = [ "frame-system-benchmarking", "frame-system-rpc-runtime-api", "hex-literal 0.3.4", - "kilt-dip-support", + "kilt-dip-primitives", "pallet-aura", "pallet-authorship", "pallet-balances", @@ -2550,7 +2550,7 @@ dependencies = [ "frame-system-benchmarking", "frame-system-rpc-runtime-api", "hex-literal 0.3.4", - "kilt-dip-support", + "kilt-dip-primitives", "kilt-runtime-api-did", "kilt-runtime-api-dip-provider", "kilt-support", @@ -4555,7 +4555,7 @@ dependencies = [ ] [[package]] -name = "kilt-dip-support" +name = "kilt-dip-primitives" version = "1.12.0-dev" dependencies = [ "cfg-if", @@ -6537,6 +6537,7 @@ dependencies = [ name = "pallet-dip-consumer" version = "1.12.0-dev" dependencies = [ + "cfg-if", "frame-benchmarking", "frame-support", "frame-system", @@ -9916,7 +9917,7 @@ dependencies = [ "frame-support", "frame-system", "kilt-asset-dids", - "kilt-dip-support", + "kilt-dip-primitives", "kilt-support", "log", "pallet-authorship", diff --git a/Cargo.toml b/Cargo.toml index afd7e6a757..402d2dea51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ substrate-wasm-builder = {git = "https://github.com/paritytech/substrate", branc # External (without extra features and with default disabled if necessary) base58 = {version = "0.2.0", default-features = false} bitflags = {version = "1.3.2", default-features = false} +cfg-if = "1.0" clap = "4.1.6" env_logger = "0.10.0" fluent-uri = { version = "0.1.4", default-features = false } @@ -68,7 +69,7 @@ pallet-migration = {path = "pallets/pallet-migration", default-features = false} # Internal support (with default disabled) kilt-asset-dids = {path = "crates/assets", default-features = false} -kilt-dip-support = {path = "crates/kilt-dip-support", default-features = false} +kilt-dip-primitives = {path = "crates/kilt-dip-primitives", default-features = false} kilt-support = {path = "support", default-features = false} runtime-common = {path = "runtimes/common", default-features = false} diff --git a/crates/kilt-dip-support/Cargo.toml b/crates/kilt-dip-primitives/Cargo.toml similarity index 90% rename from crates/kilt-dip-support/Cargo.toml rename to crates/kilt-dip-primitives/Cargo.toml index 30262359b2..2afec4987c 100644 --- a/crates/kilt-dip-support/Cargo.toml +++ b/crates/kilt-dip-primitives/Cargo.toml @@ -1,11 +1,11 @@ [package] authors.workspace = true -description = "Support types, traits, and functions for the KILT Decentralized Identity Provider (DIP) functionality as implemented by the KILT blockchain." +description = "Primitive types, traits, and functions for the KILT Decentralized Identity Provider (DIP) functionality as implemented by the KILT blockchain." documentation.workspace = true edition.workspace = true homepage.workspace = true license-file.workspace = true -name = "kilt-dip-support" +name = "kilt-dip-primitives" readme.workspace = true repository.workspace = true version.workspace = true @@ -14,7 +14,7 @@ version.workspace = true # External dependencies hash-db.workspace = true log.workspace = true -cfg-if = "1.0" +cfg-if.workspace = true # Internal dependencies did.workspace = true diff --git a/crates/kilt-dip-support/src/did.rs b/crates/kilt-dip-primitives/src/did.rs similarity index 98% rename from crates/kilt-dip-support/src/did.rs rename to crates/kilt-dip-primitives/src/did.rs index 88ec834d0e..683e952284 100644 --- a/crates/kilt-dip-support/src/did.rs +++ b/crates/kilt-dip-primitives/src/did.rs @@ -30,7 +30,7 @@ use sp_std::vec::Vec; use crate::{ merkle::RevealedDidKey, - traits::{Bump, DidSignatureVerifierContext, DipCallOriginFilter}, + traits::{DidSignatureVerifierContext, DipCallOriginFilter, Incrementable}, }; /// Type returned by the Merkle proof verifier component of the DIP consumer @@ -136,7 +136,7 @@ where ContextProvider::BlockNumber: Encode + CheckedSub + From + PartialOrd, ContextProvider::Hash: Encode, ContextProvider::SignedExtra: Encode, - DidLocalDetails: Bump + Default + Encode, + DidLocalDetails: Incrementable + Default + Encode, RemoteAccountId: Clone, MerkleProofEntries: sp_std::borrow::Borrow<[RevealedDidKey]>, CallVerifier: @@ -207,7 +207,7 @@ where } if let Some(details) = local_details { - details.bump(); + details.increment(); } else { *local_details = Some(DidLocalDetails::default()); }; diff --git a/crates/kilt-dip-support/src/export/common.rs b/crates/kilt-dip-primitives/src/export/common.rs similarity index 100% rename from crates/kilt-dip-support/src/export/common.rs rename to crates/kilt-dip-primitives/src/export/common.rs diff --git a/crates/kilt-dip-support/src/export/mod.rs b/crates/kilt-dip-primitives/src/export/mod.rs similarity index 72% rename from crates/kilt-dip-support/src/export/mod.rs rename to crates/kilt-dip-primitives/src/export/mod.rs index 01fd5ad2a1..a48871a305 100644 --- a/crates/kilt-dip-support/src/export/mod.rs +++ b/crates/kilt-dip-primitives/src/export/mod.rs @@ -16,21 +16,20 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -/// Verification logic to integrate a child chain as a DIP provider. -pub mod child; /// Verification logic to integrate a sibling chain as a DIP provider. -pub mod sibling; +pub mod parachain; +/// Verification logic to integrate a child chain as a DIP provider. +pub mod relaychain; mod common; -pub use child::{ - DipChildProviderStateProofVerifierError, KiltVersionedChildProviderVerifier, VersionedChildParachainDipStateProof, +pub use parachain::{ + DipParachainStateProofVerifierError, KiltVersionedParachainVerifier, VersionedDipParachainStateProof, }; -pub use sibling::{ - DipSiblingProviderStateProofVerifierError, KiltVersionedSiblingProviderVerifier, - VersionedSiblingParachainDipStateProof, +pub use relaychain::{ + DipRelaychainStateProofVerifierError, KiltVersionedRelaychainVerifier, VersionedRelaychainStateProof, }; pub mod latest { - pub use super::{child::latest::*, common::latest::*, sibling::latest::*}; + pub use super::{common::latest::*, parachain::latest::*, relaychain::latest::*}; } diff --git a/crates/kilt-dip-support/src/export/sibling.rs b/crates/kilt-dip-primitives/src/export/parachain.rs similarity index 88% rename from crates/kilt-dip-support/src/export/sibling.rs rename to crates/kilt-dip-primitives/src/export/parachain.rs index a67d9da56b..f0a6a63fc4 100644 --- a/crates/kilt-dip-support/src/export/sibling.rs +++ b/crates/kilt-dip-primitives/src/export/parachain.rs @@ -32,8 +32,8 @@ use sp_std::marker::PhantomData; use crate::{ did::RevealedDidKeysSignatureAndCallVerifierError, merkle::{DidMerkleProofVerifierError, RevealedDidMerkleProofLeaf, RevealedDidMerkleProofLeaves}, - state_proofs::{parachain::DipIdentityCommitmentProofVerifierError, relay_chain::ParachainHeadProofVerifierError}, - traits::{self, Bump, DidSignatureVerifierContext, DipCallOriginFilter}, + state_proofs::{parachain::DipIdentityCommitmentProofVerifierError, relaychain::ParachainHeadProofVerifierError}, + traits::{self, DidSignatureVerifierContext, DipCallOriginFilter, Incrementable}, utils::OutputOf, BoundedBlindedValue, FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet, }; @@ -43,15 +43,14 @@ use crate::{ /// /// For more info, refer to the version-specific proofs. #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] -#[non_exhaustive] -pub enum VersionedSiblingParachainDipStateProof< +pub enum VersionedDipParachainStateProof< RelayBlockHeight, DipMerkleProofBlindedValues, DipMerkleProofRevealedLeaf, LocalBlockNumber, > { V0( - v0::SiblingParachainDipStateProof< + v0::ParachainDipStateProof< RelayBlockHeight, DipMerkleProofBlindedValues, DipMerkleProofRevealedLeaf, @@ -63,7 +62,7 @@ pub enum VersionedSiblingParachainDipStateProof< #[cfg(feature = "runtime-benchmarks")] impl kilt_support::traits::GetWorstCase - for VersionedSiblingParachainDipStateProof< + for VersionedDipParachainStateProof< RelayBlockHeight, DipMerkleProofBlindedValues, DipMerkleProofRevealedLeaf, @@ -76,11 +75,11 @@ impl Self { - Self::V0(v0::SiblingParachainDipStateProof::worst_case(context)) + Self::V0(v0::ParachainDipStateProof::worst_case(context)) } } -pub enum DipSiblingProviderStateProofVerifierError< +pub enum DipParachainStateProofVerifierError< ParachainHeadMerkleProofVerificationError, IdentityCommitmentMerkleProofVerificationError, DipProofVerificationError, @@ -100,7 +99,7 @@ impl< DidSignatureVerificationError, > From< - DipSiblingProviderStateProofVerifierError< + DipParachainStateProofVerifierError< ParachainHeadMerkleProofVerificationError, IdentityCommitmentMerkleProofVerificationError, DipProofVerificationError, @@ -114,7 +113,7 @@ where DidSignatureVerificationError: Into, { fn from( - value: DipSiblingProviderStateProofVerifierError< + value: DipParachainStateProofVerifierError< ParachainHeadMerkleProofVerificationError, IdentityCommitmentMerkleProofVerificationError, DipProofVerificationError, @@ -122,15 +121,15 @@ where >, ) -> Self { match value { - DipSiblingProviderStateProofVerifierError::UnsupportedVersion => 0, - DipSiblingProviderStateProofVerifierError::ParachainHeadMerkleProof(error) => { + DipParachainStateProofVerifierError::UnsupportedVersion => 0, + DipParachainStateProofVerifierError::ParachainHeadMerkleProof(error) => { u8::MAX as u16 + error.into() as u16 } - DipSiblingProviderStateProofVerifierError::IdentityCommitmentMerkleProof(error) => { + DipParachainStateProofVerifierError::IdentityCommitmentMerkleProof(error) => { u8::MAX as u16 * 2 + error.into() as u16 } - DipSiblingProviderStateProofVerifierError::DipProof(error) => u8::MAX as u16 * 3 + error.into() as u16, - DipSiblingProviderStateProofVerifierError::DidSignature(error) => u8::MAX as u16 * 4 + error.into() 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, } } } @@ -138,7 +137,7 @@ where /// Proof verifier configured given a specific KILT runtime implementation. /// /// It is a specialization of the -/// [`GenericVersionedDipSiblingProviderStateProofVerifier`] type, with +/// [`GenericVersionedParachainVerifier`] type, with /// configurations derived from the provided KILT runtime. /// /// The generic types @@ -151,14 +150,16 @@ where /// * `LocalDidCallVerifier`: Logic to map `RuntimeCall`s to a specific DID key /// relationship. This information is used once the Merkle proof is verified, /// to filter only the revealed keys that match the provided relationship. -/// * `MAX_REVEALED_KEYS_COUNT`: Max number of DID keys that the verifier will -/// accept 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. -/// * `MAX_DID_SIGNATURE_DURATION`: Max number of blocks a cross-chain DID -/// signature is considered fresh. +/// * `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 [`GenericVersionedDipSiblingProviderStateProofVerifier`] +/// It specializes the [`GenericVersionedParachainVerifier`] /// type by using the following types for its generics: /// * `RelayChainInfo`: The provided `RelayChainInfo`. /// * `ChildProviderParachainId`: The provided `KiltParachainId`. @@ -177,15 +178,15 @@ where /// configured with the provided `KiltRuntime` and /// `MAX_DID_SIGNATURE_DURATION`. /// * `LocalDidCallVerifier`: The provided `LocalDidCallVerifier`. -pub struct KiltVersionedSiblingProviderVerifier< +pub struct KiltVersionedParachainVerifier< KiltRuntime, KiltParachainId, RelayChainStateInfo, KiltDipMerkleHasher, LocalDidCallVerifier, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, - const MAX_DID_SIGNATURE_DURATION: u16, + const MAX_REVEALED_KEYS_COUNT: u32 = 10, + const MAX_REVEALED_ACCOUNTS_COUNT: u32 = 10, + const MAX_DID_SIGNATURE_DURATION: u16 = 50, >( PhantomData<( KiltRuntime, @@ -207,7 +208,7 @@ impl< const MAX_REVEALED_ACCOUNTS_COUNT: u32, const MAX_DID_SIGNATURE_DURATION: u16, > IdentityProofVerifier - for KiltVersionedSiblingProviderVerifier< + for KiltVersionedParachainVerifier< KiltRuntime, KiltParachainId, RelayChainStateInfo, @@ -227,7 +228,7 @@ impl< KeyIdOf: Into, KiltDipMerkleHasher: sp_core::Hasher>, ConsumerRuntime: pallet_dip_consumer::Config, - ConsumerRuntime::LocalIdentityInfo: Bump + Default + Encode, + ConsumerRuntime::LocalIdentityInfo: Incrementable + Default + Encode, RelayChainStateInfo: traits::RelayChainStorageInfo + traits::RelayChainStateInfo, RelayChainStateInfo::ParaId: From, RelayChainStateInfo::BlockNumber: Parameter + 'static + Copy + Into + TryFrom + HasCompact, @@ -240,13 +241,13 @@ impl< ), >, { - type Error = DipSiblingProviderStateProofVerifierError< + type Error = DipParachainStateProofVerifierError< ParachainHeadProofVerifierError, DipIdentityCommitmentProofVerifierError, DidMerkleProofVerifierError, RevealedDidKeysSignatureAndCallVerifierError, >; - type Proof = VersionedSiblingParachainDipStateProof< + type Proof = VersionedDipParachainStateProof< RelayChainStateInfo::BlockNumber, BoundedBlindedValue, RevealedDidMerkleProofLeaf< @@ -275,7 +276,7 @@ impl< identity_details: &mut Option, proof: Self::Proof, ) -> Result { - , @@ -302,12 +303,12 @@ impl< /// versions coming from a sibling provider running one of the available KILT /// runtimes. /// -/// It expects the DIP proof to be a [`VersionedSiblingParachainDipStateProof`], +/// 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 GenericVersionedDipSiblingProviderStateProofVerifier< +pub struct GenericVersionedParachainVerifier< RelayChainStateInfo, SiblingProviderParachainId, SiblingProviderStateInfo, @@ -351,7 +352,7 @@ impl< LocalContextProvider, LocalDidCallVerifier, > IdentityProofVerifier - for GenericVersionedDipSiblingProviderStateProofVerifier< + for GenericVersionedParachainVerifier< RelayChainStateInfo, SiblingProviderParachainId, SiblingProviderStateInfo, @@ -366,7 +367,7 @@ impl< LocalDidCallVerifier, > where ConsumerRuntime: pallet_dip_consumer::Config, - ConsumerRuntime::LocalIdentityInfo: Bump + Default, + ConsumerRuntime::LocalIdentityInfo: Incrementable + Default, RelayChainStateInfo: traits::RelayChainStorageInfo + traits::RelayChainStateInfo, OutputOf: Ord, @@ -398,13 +399,13 @@ impl< ProviderLinkedAccountId: Parameter + 'static, ProviderWeb3Name: Parameter + 'static, { - type Error = DipSiblingProviderStateProofVerifierError< + type Error = DipParachainStateProofVerifierError< ParachainHeadProofVerifierError, DipIdentityCommitmentProofVerifierError, DidMerkleProofVerifierError, RevealedDidKeysSignatureAndCallVerifierError, >; - type Proof = VersionedSiblingParachainDipStateProof< + type Proof = VersionedDipParachainStateProof< RelayChainStateInfo::BlockNumber, BoundedBlindedValue, RevealedDidMerkleProofLeaf< @@ -434,7 +435,7 @@ impl< proof: Self::Proof, ) -> Result { match proof { - VersionedSiblingParachainDipStateProof::V0(v0_proof) => kilt_support::traits::GetWorstCase - for SiblingParachainDipStateProof< - RelayBlockHeight, - DipMerkleProofBlindedValues, - DipMerkleProofRevealedLeaf, - LocalBlockNumber, - > where + for ParachainDipStateProof + where DipMerkleProofBlindedValues: kilt_support::traits::GetWorstCase, DipMerkleProofRevealedLeaf: Default + Clone, RelayBlockHeight: Default, @@ -532,7 +529,7 @@ pub mod v0 { /// different verifier or a wrapper around this verifier must be built. /// /// It expects the DIP proof to be a - /// [`VersionedSiblingParachainDipStateProof`], and returns + /// [`VersionedDipParachainStateProof`], and returns /// [`RevealedDidMerkleProofLeaves`] if the proof is successfully verified. /// This information is then made availabe as an origin to the downstream /// call dispatched. @@ -588,7 +585,7 @@ pub mod v0 { /// verified, to filter only the revealed keys that match the provided /// relationship. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] - pub struct DipSiblingProviderStateProofVerifier< + pub struct ParachainVerifier< RelayChainStateInfo, SiblingProviderParachainId, SiblingProviderStateInfo, @@ -632,7 +629,7 @@ pub mod v0 { LocalContextProvider, LocalDidCallVerifier, > IdentityProofVerifier - for DipSiblingProviderStateProofVerifier< + for ParachainVerifier< RelayChainStateInfo, SiblingProviderParachainId, SiblingProviderStateInfo, @@ -647,7 +644,7 @@ pub mod v0 { LocalDidCallVerifier, > where ConsumerRuntime: pallet_dip_consumer::Config, - ConsumerRuntime::LocalIdentityInfo: Bump + Default, + ConsumerRuntime::LocalIdentityInfo: Incrementable + Default, RelayChainStateInfo: traits::RelayChainStorageInfo + traits::RelayChainStateInfo, OutputOf: Ord, @@ -679,13 +676,13 @@ pub mod v0 { ProviderLinkedAccountId: Parameter + 'static, ProviderWeb3Name: Parameter + 'static, { - type Error = DipSiblingProviderStateProofVerifierError< + type Error = DipParachainStateProofVerifierError< ParachainHeadProofVerifierError, DipIdentityCommitmentProofVerifierError, DidMerkleProofVerifierError, RevealedDidKeysSignatureAndCallVerifierError, >; - type Proof = SiblingParachainDipStateProof< + type Proof = ParachainDipStateProof< RelayChainStateInfo::BlockNumber, BoundedBlindedValue, RevealedDidMerkleProofLeaf< @@ -714,23 +711,23 @@ pub mod v0 { identity_details: &mut Option, proof: Self::Proof, ) -> Result { - // 1. Verify relay chain proof. + // 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, ) - .map_err(DipSiblingProviderStateProofVerifierError::ParachainHeadMerkleProof)?; + .map_err(DipParachainStateProofVerifierError::ParachainHeadMerkleProof)?; - // 2. Verify parachain state proof. + // 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(DipSiblingProviderStateProofVerifierError::IdentityCommitmentMerkleProof)?; + .map_err(DipParachainStateProofVerifierError::IdentityCommitmentMerkleProof)?; // 3. Verify DIP merkle proof. let proof_leaves: RevealedDidMerkleProofLeaves< @@ -751,9 +748,9 @@ pub mod v0 { MAX_REVEALED_KEYS_COUNT, MAX_REVEALED_ACCOUNTS_COUNT, >(&subject_identity_commitment, proof.did.leaves) - .map_err(DipSiblingProviderStateProofVerifierError::DipProof)?; + .map_err(DipParachainStateProofVerifierError::DipProof)?; - // 4. Verify DID signature. + // 4. Verify call is signed by one of the DID keys revealed at step 3. verify_did_signature_for_call::<_, _, _, _, LocalContextProvider, _, _, _, LocalDidCallVerifier>( call, submitter, @@ -763,7 +760,7 @@ pub mod v0 { did_signature: proof.did.signature, }, ) - .map_err(DipSiblingProviderStateProofVerifierError::DidSignature)?; + .map_err(DipParachainStateProofVerifierError::DidSignature)?; Ok(proof_leaves) } diff --git a/crates/kilt-dip-support/src/export/child.rs b/crates/kilt-dip-primitives/src/export/relaychain.rs similarity index 88% rename from crates/kilt-dip-support/src/export/child.rs rename to crates/kilt-dip-primitives/src/export/relaychain.rs index 58321c11bb..ce75fc5cfa 100644 --- a/crates/kilt-dip-support/src/export/child.rs +++ b/crates/kilt-dip-primitives/src/export/relaychain.rs @@ -32,10 +32,10 @@ use sp_std::marker::PhantomData; use crate::{ did::RevealedDidKeysSignatureAndCallVerifierError, merkle::{DidMerkleProofVerifierError, RevealedDidMerkleProofLeaf, RevealedDidMerkleProofLeaves}, - state_proofs::{parachain::DipIdentityCommitmentProofVerifierError, relay_chain::ParachainHeadProofVerifierError}, + state_proofs::{parachain::DipIdentityCommitmentProofVerifierError, relaychain::ParachainHeadProofVerifierError}, traits::{ - Bump, DidSignatureVerifierContext, DipCallOriginFilter, HistoricalBlockRegistry, ProviderParachainStorageInfo, - RelayChainStorageInfo, + DidSignatureVerifierContext, DipCallOriginFilter, HistoricalBlockRegistry, Incrementable, + ProviderParachainStorageInfo, RelayChainStorageInfo, }, utils::OutputOf, BoundedBlindedValue, FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet, @@ -46,15 +46,14 @@ use crate::{ /// /// For more info, refer to the version-specific proofs. #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, Clone)] -#[non_exhaustive] -pub enum VersionedChildParachainDipStateProof< +pub enum VersionedRelaychainStateProof< ParentBlockHeight: Copy + Into + TryFrom, ParentBlockHasher: Hash, DipMerkleProofBlindedValues, DipMerkleProofRevealedLeaf, > { V0( - v0::ChildParachainDipStateProof< + v0::RelaychainDipStateProof< ParentBlockHeight, ParentBlockHasher, DipMerkleProofBlindedValues, @@ -63,7 +62,7 @@ pub enum VersionedChildParachainDipStateProof< ), } -pub enum DipChildProviderStateProofVerifierError< +pub enum DipRelaychainStateProofVerifierError< ParachainHeadMerkleProofVerificationError, IdentityCommitmentMerkleProofVerificationError, DipProofVerificationError, @@ -85,7 +84,7 @@ impl< DidSignatureVerificationError, > From< - DipChildProviderStateProofVerifierError< + DipRelaychainStateProofVerifierError< ParachainHeadMerkleProofVerificationError, IdentityCommitmentMerkleProofVerificationError, DipProofVerificationError, @@ -99,7 +98,7 @@ where DidSignatureVerificationError: Into, { fn from( - value: DipChildProviderStateProofVerifierError< + value: DipRelaychainStateProofVerifierError< ParachainHeadMerkleProofVerificationError, IdentityCommitmentMerkleProofVerificationError, DipProofVerificationError, @@ -107,17 +106,17 @@ where >, ) -> Self { match value { - DipChildProviderStateProofVerifierError::UnsupportedVersion => 0, - DipChildProviderStateProofVerifierError::InvalidBlockHeight => 1, - DipChildProviderStateProofVerifierError::InvalidBlockHash => 2, - DipChildProviderStateProofVerifierError::ParachainHeadMerkleProof(error) => { + DipRelaychainStateProofVerifierError::UnsupportedVersion => 0, + DipRelaychainStateProofVerifierError::InvalidBlockHeight => 1, + DipRelaychainStateProofVerifierError::InvalidBlockHash => 2, + DipRelaychainStateProofVerifierError::ParachainHeadMerkleProof(error) => { u8::MAX as u16 + error.into() as u16 } - DipChildProviderStateProofVerifierError::IdentityCommitmentMerkleProof(error) => { + DipRelaychainStateProofVerifierError::IdentityCommitmentMerkleProof(error) => { u8::MAX as u16 * 2 + error.into() as u16 } - DipChildProviderStateProofVerifierError::DipProof(error) => u8::MAX as u16 * 3 + error.into() as u16, - DipChildProviderStateProofVerifierError::DidSignature(error) => u8::MAX as u16 * 4 + error.into() 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, } } } @@ -125,7 +124,7 @@ where /// Proof verifier configured given a specific KILT runtime implementation. /// /// A specialization of the -/// [`GenericVersionedDipChildProviderStateProofVerifier`] type, with +/// [`GenericVersionedRelaychainVerifier`] type, with /// configurations derived from the provided KILT runtime. /// /// The generic types are the following: @@ -138,14 +137,16 @@ where /// * `LocalDidCallVerifier`: Logic to map `RuntimeCall`s to a specific DID key /// relationship. This information is used once the Merkle proof is verified, /// to filter only the revealed keys that match the provided relationship. -/// * `MAX_REVEALED_KEYS_COUNT`: Max number of DID keys that the verifier will -/// accept 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. -/// * `MAX_DID_SIGNATURE_DURATION`: Max number of blocks a cross-chain DID -/// signature is considered fresh. +/// * `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 [`GenericVersionedDipChildProviderStateProofVerifier`] +/// It specializes the [`GenericVersionedRelaychainVerifier`] /// type by using the following types for its generics: /// * `RelayChainInfo`: The provided `RelayChainInfo`. /// * `ChildProviderParachainId`: The provided `KiltParachainId`. @@ -164,15 +165,15 @@ where /// configured with the provided `KiltRuntime` and /// `MAX_DID_SIGNATURE_DURATION`. /// * `LocalDidCallVerifier`: The provided `LocalDidCallVerifier`. -pub struct KiltVersionedChildProviderVerifier< +pub struct KiltVersionedRelaychainVerifier< KiltRuntime, KiltParachainId, RelayChainInfo, KiltDipMerkleHasher, LocalDidCallVerifier, - const MAX_REVEALED_KEYS_COUNT: u32, - const MAX_REVEALED_ACCOUNTS_COUNT: u32, - const MAX_DID_SIGNATURE_DURATION: u16, + const MAX_REVEALED_KEYS_COUNT: u32 = 10, + const MAX_REVEALED_ACCOUNTS_COUNT: u32 = 10, + const MAX_DID_SIGNATURE_DURATION: u16 = 50, >( PhantomData<( KiltRuntime, @@ -194,7 +195,7 @@ impl< const MAX_REVEALED_ACCOUNTS_COUNT: u32, const MAX_DID_SIGNATURE_DURATION: u16, > IdentityProofVerifier - for KiltVersionedChildProviderVerifier< + for KiltVersionedRelaychainVerifier< KiltRuntime, KiltParachainId, RelayChainInfo, @@ -214,7 +215,7 @@ impl< KeyIdOf: Into, KiltDipMerkleHasher: sp_core::Hasher>, ConsumerRuntime: pallet_dip_consumer::Config, - ConsumerRuntime::LocalIdentityInfo: Bump + Default + Encode, + ConsumerRuntime::LocalIdentityInfo: Incrementable + Default + Encode, RelayChainInfo: RelayChainStorageInfo> + HistoricalBlockRegistry< BlockNumber = ::BlockNumber, @@ -244,13 +245,13 @@ impl< ), >, { - type Error = DipChildProviderStateProofVerifierError< + type Error = DipRelaychainStateProofVerifierError< ParachainHeadProofVerifierError, DipIdentityCommitmentProofVerifierError, DidMerkleProofVerifierError, RevealedDidKeysSignatureAndCallVerifierError, >; - type Proof = VersionedChildParachainDipStateProof< + type Proof = VersionedRelaychainStateProof< ::BlockNumber, ::Hasher, BoundedBlindedValue, @@ -279,7 +280,7 @@ impl< identity_details: &mut Option, proof: Self::Proof, ) -> Result { - , @@ -306,12 +307,12 @@ impl< /// versions coming from a child provider running one of the available KILT /// runtimes. /// -/// It expects the DIP proof to be a [`VersionedChildParachainDipStateProof`], +/// 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 GenericVersionedDipChildProviderStateProofVerifier< +pub struct GenericVersionedRelaychainVerifier< RelayChainInfo, ChildProviderParachainId, ChildProviderStateInfo, @@ -355,7 +356,7 @@ impl< LocalContextProvider, LocalDidCallVerifier, > IdentityProofVerifier - for GenericVersionedDipChildProviderStateProofVerifier< + for GenericVersionedRelaychainVerifier< RelayChainInfo, ChildProviderParachainId, ChildProviderStateInfo, @@ -370,7 +371,7 @@ impl< LocalDidCallVerifier, > where ConsumerRuntime: pallet_dip_consumer::Config, - ConsumerRuntime::LocalIdentityInfo: Bump + Default, + ConsumerRuntime::LocalIdentityInfo: Incrementable + Default, RelayChainInfo: RelayChainStorageInfo> + HistoricalBlockRegistry< @@ -418,13 +419,13 @@ impl< ProviderLinkedAccountId: Parameter + 'static, ProviderWeb3Name: Parameter + 'static, { - type Error = DipChildProviderStateProofVerifierError< + type Error = DipRelaychainStateProofVerifierError< ParachainHeadProofVerifierError, DipIdentityCommitmentProofVerifierError, DidMerkleProofVerifierError, RevealedDidKeysSignatureAndCallVerifierError, >; - type Proof = VersionedChildParachainDipStateProof< + type Proof = VersionedRelaychainStateProof< ::BlockNumber, ::Hasher, BoundedBlindedValue, @@ -454,7 +455,7 @@ impl< proof: Self::Proof, ) -> Result { match proof { - VersionedChildParachainDipStateProof::V0(v0_proof) => + TryFrom, ParentBlockHasher: Hash, DipMerkleProofBlindedValues, @@ -545,7 +546,7 @@ pub mod v0 { /// different verifier or a wrapper around this verifier must be built. /// /// It expects the DIP proof to be a - /// [`VersionedChildParachainDipStateProof`], and returns + /// [`VersionedRelaychainStateProof`], and returns /// [`RevealedDidMerkleProofLeaves`] if the proof is successfully verified. /// This information is then made availabe as an origin to the downstream /// call dispatched. @@ -602,7 +603,7 @@ pub mod v0 { /// key relationship. This information is used once the Merkle proof is /// verified, to filter only the revealed keys that match the provided /// relationship. - pub struct DipChildProviderStateProofVerifier< + pub struct RelaychainVerifier< RelayChainInfo, ChildProviderParachainId, ChildProviderStateInfo, @@ -646,7 +647,7 @@ pub mod v0 { LocalContextProvider, LocalDidCallVerifier, > IdentityProofVerifier - for DipChildProviderStateProofVerifier< + for RelaychainVerifier< RelayChainInfo, ChildProviderParachainId, ChildProviderStateInfo, @@ -661,7 +662,7 @@ pub mod v0 { LocalDidCallVerifier, > where ConsumerRuntime: pallet_dip_consumer::Config, - ConsumerRuntime::LocalIdentityInfo: Bump + Default, + ConsumerRuntime::LocalIdentityInfo: Incrementable + Default, RelayChainInfo: RelayChainStorageInfo> + HistoricalBlockRegistry< @@ -710,13 +711,13 @@ pub mod v0 { ProviderLinkedAccountId: Parameter + 'static, ProviderWeb3Name: Parameter + 'static, { - type Error = DipChildProviderStateProofVerifierError< + type Error = DipRelaychainStateProofVerifierError< ParachainHeadProofVerifierError, DipIdentityCommitmentProofVerifierError, DidMerkleProofVerifierError, RevealedDidKeysSignatureAndCallVerifierError, >; - type Proof = ChildParachainDipStateProof< + type Proof = RelaychainDipStateProof< ::BlockNumber, ::Hasher, BoundedBlindedValue, @@ -747,11 +748,11 @@ pub mod v0 { ) -> 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(DipChildProviderStateProofVerifierError::InvalidBlockHeight)?; + .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(DipChildProviderStateProofVerifierError::InvalidBlockHash); + 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; @@ -763,7 +764,7 @@ pub mod v0 { &state_root_at_height, proof.para_state_root.proof, ) - .map_err(DipChildProviderStateProofVerifierError::ParachainHeadMerkleProof)?; + .map_err(DipRelaychainStateProofVerifierError::ParachainHeadMerkleProof)?; // 3. Verify parachain state proof. let subject_identity_commitment = @@ -772,7 +773,7 @@ pub mod v0 { provider_parachain_header.state_root.into(), proof.dip_identity_commitment, ) - .map_err(DipChildProviderStateProofVerifierError::IdentityCommitmentMerkleProof)?; + .map_err(DipRelaychainStateProofVerifierError::IdentityCommitmentMerkleProof)?; // 4. Verify DIP merkle proof. let proof_leaves = verify_dip_merkle_proof::< @@ -785,7 +786,7 @@ pub mod v0 { MAX_REVEALED_KEYS_COUNT, MAX_REVEALED_ACCOUNTS_COUNT, >(&subject_identity_commitment, proof.did.leaves) - .map_err(DipChildProviderStateProofVerifierError::DipProof)?; + .map_err(DipRelaychainStateProofVerifierError::DipProof)?; // 5. Verify DID signature. verify_did_signature_for_call::<_, _, _, _, LocalContextProvider, _, _, _, LocalDidCallVerifier>( @@ -797,7 +798,7 @@ pub mod v0 { did_signature: proof.did.signature, }, ) - .map_err(DipChildProviderStateProofVerifierError::DidSignature)?; + .map_err(DipRelaychainStateProofVerifierError::DidSignature)?; Ok(proof_leaves) } } diff --git a/crates/kilt-dip-support/src/lib.rs b/crates/kilt-dip-primitives/src/lib.rs similarity index 84% rename from crates/kilt-dip-support/src/lib.rs rename to crates/kilt-dip-primitives/src/lib.rs index 66ec155cdc..04c9f17a25 100644 --- a/crates/kilt-dip-support/src/lib.rs +++ b/crates/kilt-dip-primitives/src/lib.rs @@ -21,8 +21,8 @@ //! protocol. //! //! Consumers of KILT identities should prefer directly using -//! [`KiltVersionedChildProviderVerifier`] for consumer relaychains and -//! [`KiltVersionedSiblingProviderVerifier`] for consumer sibling parachains. +//! [`KiltVersionedRelaychainVerifier`] for consumer relaychains and +//! [`KiltVersionedParachainVerifier`] for consumer sibling parachains. #![cfg_attr(not(feature = "std"), no_std)] @@ -37,7 +37,7 @@ mod export; pub use export::*; pub use state_proofs::{ parachain::{DipIdentityCommitmentProofVerifier, DipIdentityCommitmentProofVerifierError}, - relay_chain::{ParachainHeadProofVerifier, ParachainHeadProofVerifierError, RelayStateRootsViaRelayStorePallet}, + relaychain::{ParachainHeadProofVerifier, ParachainHeadProofVerifierError, RelayStateRootsViaRelayStorePallet}, }; pub use traits::{FrameSystemDidSignatureContext, ProviderParachainStateInfoViaProviderPallet}; pub use utils::BoundedBlindedValue; diff --git a/crates/kilt-dip-support/src/merkle.rs b/crates/kilt-dip-primitives/src/merkle.rs similarity index 100% rename from crates/kilt-dip-support/src/merkle.rs rename to crates/kilt-dip-primitives/src/merkle.rs diff --git a/crates/kilt-dip-primitives/src/state_proofs/mod.rs b/crates/kilt-dip-primitives/src/state_proofs/mod.rs new file mode 100644 index 0000000000..f21a27b147 --- /dev/null +++ b/crates/kilt-dip-primitives/src/state_proofs/mod.rs @@ -0,0 +1,81 @@ +// 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 state proofs. + +/// Parachain-related state proof logic. +pub(crate) mod parachain; +/// Relaychain-related state proof logic. +pub(crate) mod relaychain; + +// Ported from https://github.com/paritytech/substrate/blob/b27c470eaff379f512d1dec052aff5d551ed3b03/primitives/state-machine/src/lib.rs#L1076 +// 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); + } + 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/state_proofs/parachain.rs b/crates/kilt-dip-primitives/src/state_proofs/parachain.rs new file mode 100644 index 0000000000..3ad51695f0 --- /dev/null +++ b/crates/kilt-dip-primitives/src/state_proofs/parachain.rs @@ -0,0 +1,168 @@ +// 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 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 new file mode 100644 index 0000000000..018b98481b --- /dev/null +++ b/crates/kilt-dip-primitives/src/state_proofs/relaychain.rs @@ -0,0 +1,261 @@ +// 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 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-support/src/traits.rs b/crates/kilt-dip-primitives/src/traits.rs similarity index 93% rename from crates/kilt-dip-support/src/traits.rs rename to crates/kilt-dip-primitives/src/traits.rs index 7d0967c797..b96ac7d33a 100644 --- a/crates/kilt-dip-support/src/traits.rs +++ b/crates/kilt-dip-primitives/src/traits.rs @@ -26,25 +26,25 @@ use crate::utils::OutputOf; // TODO: Switch to the `Incrementable` trait once it's added to the root of // `frame_support`. -/// A trait for "bumpable" types, i.e., types that have some notion of order of -/// its members. -pub trait Bump { - /// Bump the type instance to its next value. Overflows are assumed to be - /// taken care of by the type internal logic. - fn bump(&mut self); +/// A trait for "incrementable" types, i.e., types that have some notion of +/// order of its members. +pub trait Incrementable { + /// Increment the type instance to its next value. Overflows are assumed to + /// be taken care of by the type internal logic. + fn increment(&mut self); } -impl Bump for T +impl Incrementable for T where T: CheckedAdd + Zero + One, { - fn bump(&mut self) { + fn increment(&mut self) { *self = self.checked_add(&Self::one()).unwrap_or_else(Self::zero); } } -/// A trait for types that implement some sort of access control logic on the -/// provided input `Call` type. +/// A trait for types that implement access control logic where the call is the +/// controlled resource and access is granted based on the provided info. /// The generic types are the following: /// * `Call`: The type of the call being checked. pub trait DipCallOriginFilter { diff --git a/crates/kilt-dip-support/src/utils.rs b/crates/kilt-dip-primitives/src/utils.rs similarity index 100% rename from crates/kilt-dip-support/src/utils.rs rename to crates/kilt-dip-primitives/src/utils.rs diff --git a/crates/kilt-dip-support/src/state_proofs.rs b/crates/kilt-dip-support/src/state_proofs.rs deleted file mode 100644 index 017470aa6e..0000000000 --- a/crates/kilt-dip-support/src/state_proofs.rs +++ /dev/null @@ -1,483 +0,0 @@ -// KILT Blockchain – https://botlabs.org -// Copyright (C) 2019-2023 BOTLabs GmbH - -// The KILT Blockchain is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// The KILT Blockchain is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// If you feel like getting in touch with us, you can do so at info@botlabs.org - -//! Module to deal with cross-chain state proofs. - -use parity_scale_codec::{Decode, Encode, HasCompact}; -use sp_core::{storage::StorageKey, RuntimeDebug, U256}; -use sp_runtime::generic::Header; -use sp_std::{marker::PhantomData, vec::Vec}; -use sp_trie::StorageProof; - -use crate::utils::OutputOf; - -use substrate_no_std_port::read_proof_check; - -// Ported from https://github.com/paritytech/substrate/blob/b27c470eaff379f512d1dec052aff5d551ed3b03/primitives/state-machine/src/lib.rs#L1076 -// 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 super::*; - - 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; - use sp_trie::{HashDBT, MemoryDB}; - - 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(()) - } - } -} - -/// Relaychain-related state proof logic. -pub(super) mod relay_chain { - use super::*; - - use sp_runtime::traits::BlakeTwo256; - - use crate::traits::{RelayChainStateInfo, RelayChainStorageInfo}; - - #[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> { - 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> { - 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: It's not possible to access the runtime definition from here. - 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."); - } - } -} - -/// Parachain-related state proof logic. -pub(super) mod parachain { - use super::*; - - use crate::traits::ProviderParachainStorageInfo; - - #[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 { - 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::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/dip-template/pallets/pallet-postit/src/lib.rs b/dip-template/pallets/pallet-postit/src/lib.rs index 170797f61e..f0697f78df 100644 --- a/dip-template/pallets/pallet-postit/src/lib.rs +++ b/dip-template/pallets/pallet-postit/src/lib.rs @@ -39,7 +39,7 @@ pub mod pallet { use crate::{ post::{Comment, Post}, - traits::Usernamable, + traits::GetUsername, }; const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); @@ -52,7 +52,7 @@ pub mod pallet { pub trait Config: frame_system::Config { type MaxTextLength: Get; type OriginCheck: EnsureOrigin<::RuntimeOrigin, Success = Self::OriginSuccess>; - type OriginSuccess: Usernamable; + type OriginSuccess: GetUsername; type RuntimeEvent: From> + IsType<::RuntimeEvent>; type Username: Encode + Decode + TypeInfo + MaxEncodedLen + Clone + PartialEq + Debug + Default; } diff --git a/dip-template/pallets/pallet-postit/src/traits.rs b/dip-template/pallets/pallet-postit/src/traits.rs index 8a1d20f580..d3c1a2b9a6 100644 --- a/dip-template/pallets/pallet-postit/src/traits.rs +++ b/dip-template/pallets/pallet-postit/src/traits.rs @@ -16,7 +16,7 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org -pub trait Usernamable { +pub trait GetUsername { type Username; fn username(&self) -> Result; diff --git a/dip-template/runtimes/dip-consumer/Cargo.toml b/dip-template/runtimes/dip-consumer/Cargo.toml index ca55d7dcad..f6b7d428d4 100644 --- a/dip-template/runtimes/dip-consumer/Cargo.toml +++ b/dip-template/runtimes/dip-consumer/Cargo.toml @@ -20,7 +20,7 @@ scale-info = {workspace = true, features = ["derive"]} # DIP dip-provider-runtime-template.workspace = true did.workspace = true -kilt-dip-support.workspace = true +kilt-dip-primitives.workspace = true pallet-did-lookup.workspace = true pallet-dip-consumer.workspace = true pallet-postit.workspace = true @@ -79,7 +79,7 @@ std = [ "scale-info/std", "dip-provider-runtime-template/std", "did/std", - "kilt-dip-support/std", + "kilt-dip-primitives/std", "pallet-did-lookup/std", "pallet-dip-consumer/std", "pallet-postit/std", @@ -123,7 +123,7 @@ std = [ runtime-benchmarks = [ "dip-provider-runtime-template/runtime-benchmarks", - "kilt-dip-support/runtime-benchmarks", + "kilt-dip-primitives/runtime-benchmarks", "pallet-dip-consumer/runtime-benchmarks", "pallet-relay-store/runtime-benchmarks", "runtime-common/runtime-benchmarks", diff --git a/dip-template/runtimes/dip-consumer/src/dip.rs b/dip-template/runtimes/dip-consumer/src/dip.rs index 823fcd37f3..00868b9028 100644 --- a/dip-template/runtimes/dip-consumer/src/dip.rs +++ b/dip-template/runtimes/dip-consumer/src/dip.rs @@ -20,8 +20,8 @@ use did::{did_details::DidVerificationKey, DidVerificationKeyRelationship}; use dip_provider_runtime_template::{AccountId as ProviderAccountId, Runtime as ProviderRuntime}; use frame_support::traits::Contains; use frame_system::EnsureSigned; -use kilt_dip_support::{ - traits::DipCallOriginFilter, KiltVersionedSiblingProviderVerifier, RelayStateRootsViaRelayStorePallet, +use kilt_dip_primitives::{ + traits::DipCallOriginFilter, KiltVersionedParachainVerifier, RelayStateRootsViaRelayStorePallet, }; use pallet_dip_consumer::traits::IdentityProofVerifier; use sp_core::ConstU32; @@ -32,17 +32,15 @@ use crate::{weights, AccountId, DidIdentifier, Runtime, RuntimeCall, RuntimeOrig pub type MerkleProofVerifierOutput = >::VerificationResult; /// The verifier logic assumes the provider is a sibling KILT parachain, and /// that a KILT subject can provide DIP proof that reveal at most 10 DID keys -/// and 10 linked accounts. Calls that do not pass the [`DipCallFilter`] will be -/// discarded early on in the verification process. -pub type ProofVerifier = KiltVersionedSiblingProviderVerifier< +/// 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>, RelayStateRootsViaRelayStorePallet, BlakeTwo256, DipCallFilter, - 10, - 10, - 50, >; impl pallet_dip_consumer::Config for Runtime { diff --git a/dip-template/runtimes/dip-consumer/src/origin_adapter.rs b/dip-template/runtimes/dip-consumer/src/origin_adapter.rs index 5e0319f62e..18d720c1f0 100644 --- a/dip-template/runtimes/dip-consumer/src/origin_adapter.rs +++ b/dip-template/runtimes/dip-consumer/src/origin_adapter.rs @@ -19,7 +19,7 @@ use crate::{AccountId, DidIdentifier, MerkleProofVerifierOutput, RuntimeOrigin, Web3Name}; use frame_support::traits::EnsureOrigin; use pallet_dip_consumer::{DipOrigin, EnsureDipOrigin}; -use pallet_postit::traits::Usernamable; +use pallet_postit::traits::GetUsername; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_core::RuntimeDebug; @@ -49,7 +49,7 @@ impl EnsureOrigin for EnsureDipOriginAdapter { #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct DipOriginAdapter(DipOrigin); -impl Usernamable for DipOriginAdapter { +impl GetUsername for DipOriginAdapter { type Username = Web3Name; fn username(&self) -> Result { diff --git a/dip-template/runtimes/dip-provider/Cargo.toml b/dip-template/runtimes/dip-provider/Cargo.toml index d1f7c7bcde..63b4302ffe 100644 --- a/dip-template/runtimes/dip-provider/Cargo.toml +++ b/dip-template/runtimes/dip-provider/Cargo.toml @@ -21,7 +21,7 @@ scale-info = {workspace = true, features = ["derive"]} # DIP did.workspace = true kilt-support.workspace = true -kilt-dip-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 @@ -82,7 +82,7 @@ std = [ "scale-info/std", "did/std", "kilt-support/std", - "kilt-dip-support/std", + "kilt-dip-primitives/std", "kilt-runtime-api-did/std", "kilt-runtime-api-dip-provider/std", "pallet-deposit-storage/std", @@ -127,7 +127,7 @@ std = [ ] runtime-benchmarks = [ "did/runtime-benchmarks", - "kilt-dip-support/runtime-benchmarks", + "kilt-dip-primitives/runtime-benchmarks", "pallet-deposit-storage/runtime-benchmarks", "pallet-did-lookup/runtime-benchmarks", "pallet-dip-provider/runtime-benchmarks", diff --git a/dip-template/runtimes/dip-provider/src/dip.rs b/dip-template/runtimes/dip-provider/src/dip.rs index 49764ce340..a36cbd3944 100644 --- a/dip-template/runtimes/dip-provider/src/dip.rs +++ b/dip-template/runtimes/dip-provider/src/dip.rs @@ -126,12 +126,14 @@ pub mod deposit { fn on_deposit_reclaimed( _namespace: &::Namespace, key: &DepositKeyOf, - _deposit: DepositEntryOf, + deposit: DepositEntryOf, ) -> Result<(), Self::Error> { let (identifier, commitment_version) = <(DidIdentifier, IdentityCommitmentVersion)>::decode(&mut &key[..]) .map_err(|_| CommitmentDepositRemovalHookError::DecodeKey)?; pallet_dip_provider::Pallet::::delete_identity_commitment_storage_entry( &identifier, + // Deposit owner is the only one authorized to remove the deposit. + &deposit.deposit.owner, commitment_version, ) .map_err(|_| { diff --git a/pallets/pallet-deposit-storage/src/deposit.rs b/pallets/pallet-deposit-storage/src/deposit.rs index 36b902df5b..ef48f32f41 100644 --- a/pallets/pallet-deposit-storage/src/deposit.rs +++ b/pallets/pallet-deposit-storage/src/deposit.rs @@ -36,9 +36,9 @@ use crate::{BalanceOf, Config, Error, HoldReason, Pallet}; #[derive(Clone, Debug, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo, MaxEncodedLen)] pub struct DepositEntry { /// The [`Deposit`] entry. - pub(crate) deposit: Deposit, + pub deposit: Deposit, /// The `Reason` for the deposit. - pub(crate) reason: Reason, + pub reason: Reason, } /// Type implementing the [`DipProviderHooks`] hooks trait by taking a deposit diff --git a/pallets/pallet-dip-consumer/Cargo.toml b/pallets/pallet-dip-consumer/Cargo.toml index f19f17ab72..69695cfbee 100644 --- a/pallets/pallet-dip-consumer/Cargo.toml +++ b/pallets/pallet-dip-consumer/Cargo.toml @@ -19,6 +19,7 @@ sp-keystore = {workspace = true, features = ["std"]} sp-runtime = {workspace = true, features = ["std"]} [dependencies] +cfg-if.workspace = true frame-support.workspace = true frame-system.workspace = true kilt-support.workspace = true diff --git a/pallets/pallet-dip-consumer/src/lib.rs b/pallets/pallet-dip-consumer/src/lib.rs index 148eb88b82..ea6125d7b4 100644 --- a/pallets/pallet-dip-consumer/src/lib.rs +++ b/pallets/pallet-dip-consumer/src/lib.rs @@ -38,7 +38,7 @@ pub mod pallet { use super::*; use frame_support::{ - dispatch::{Dispatchable, GetDispatchInfo}, + dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}, pallet_prelude::*, traits::{Contains, EnsureOriginWithArg}, Twox64Concat, @@ -93,7 +93,9 @@ pub mod pallet { /// proof. type ProofVerifier: IdentityProofVerifier; /// The aggregated `Call` type. - type RuntimeCall: Parameter + Dispatchable::RuntimeOrigin> + GetDispatchInfo; + type RuntimeCall: Parameter + + Dispatchable::RuntimeOrigin> + + GetDispatchInfo; /// The aggregated `Origin` type, which must include the origin exposed /// by this pallet. type RuntimeOrigin: From> + From<::RuntimeOrigin>; @@ -149,20 +151,24 @@ pub mod pallet { identifier: T::Identifier, proof: IdentityProofOf, call: Box>, - ) -> DispatchResult { + ) -> DispatchResultWithPostInfo { let submitter = T::DispatchOriginCheck::ensure_origin(origin, &identifier)?; ensure!(T::DipCallOriginFilter::contains(&*call), Error::::Filtered); - let mut identity_entry = IdentityEntries::::get(&identifier); - let proof_verification_result = T::ProofVerifier::verify_proof_for_call_against_details( - &*call, - &identifier, - &submitter, - &mut identity_entry, - proof, - ) - .map_err(|e| Error::::InvalidProof(e.into()))?; - IdentityEntries::::mutate(&identifier, |entry| *entry = identity_entry); - let did_origin = DipOrigin { + let proof_verification_result = IdentityEntries::::try_mutate(&identifier, |identity_entry| { + T::ProofVerifier::verify_proof_for_call_against_details( + &*call, + &identifier, + &submitter, + identity_entry, + proof, + ) + .map_err(|e| Error::::InvalidProof(e.into())) + })?; + let did_origin: DipOrigin< + T::Identifier, + T::AccountId, + >::VerificationResult, + > = DipOrigin { identifier, account_address: submitter, details: proof_verification_result, @@ -171,9 +177,13 @@ pub mod pallet { // TODO: Maybe find a nicer way to exclude the call dispatched from the // benchmarks while making sure the call is actually dispatched and passes any // filters the consumer proof verifier has set. - #[cfg(not(feature = "runtime-benchmark"))] - let _ = call.dispatch(did_origin.into()).map_err(|e| e.error)?; - Ok(()) + cfg_if::cfg_if! { + if #[cfg(not(feature = "runtime-benchmark"))] { + call.dispatch(did_origin.into()) + } else { + ().into() + } + } } } } diff --git a/pallets/pallet-dip-provider/src/lib.rs b/pallets/pallet-dip-provider/src/lib.rs index e6eea5e5a8..aff9f53ef5 100644 --- a/pallets/pallet-dip-provider/src/lib.rs +++ b/pallets/pallet-dip-provider/src/lib.rs @@ -158,30 +158,22 @@ pub mod pallet { T::IdentityCommitmentGenerator::generate_commitment(&identifier, &identity, commitment_version) .map_err(|error| Error::::IdentityCommitmentGenerator(error.into()))?; - IdentityCommitments::::try_mutate(&identifier, commitment_version, |commitment_entry| { - if let Some(old_commitment) = commitment_entry { - T::ProviderHooks::on_commitment_removed( - &identifier, - &dispatcher, - old_commitment, - commitment_version, - ) - .map_err(|e| Error::::Hook(e.into()))?; - Self::deposit_event(Event::::VersionedIdentityDeleted { - identifier: identifier.clone(), - version: commitment_version, - }); - } - T::ProviderHooks::on_identity_committed(&identifier, &dispatcher, &commitment, commitment_version) - .map_err(|e| Error::::Hook(e.into()))?; - *commitment_entry = Some(commitment.clone()); - Self::deposit_event(Event::::VersionedIdentityCommitted { - identifier: identifier.clone(), - commitment, - version: commitment_version, - }); - Ok::<_, Error>(()) - })?; + match Self::delete_identity_commitment_storage_entry(&identifier, &dispatcher, commitment_version) { + // Ignore if there was no previous commitment. + Ok(_) | Err(Error::::CommitmentNotFound) => (), + // If a different error is returned, bubble it up. + Err(e) => return Err(e.into()), + }; + + IdentityCommitments::::insert(&identifier, commitment_version, commitment.clone()); + // Call hooks for new commitment. + T::ProviderHooks::on_identity_committed(&identifier, &dispatcher, &commitment, commitment_version) + .map_err(|e| Error::::Hook(e.into()))?; + Self::deposit_event(Event::::VersionedIdentityCommitted { + identifier: identifier.clone(), + commitment, + version: commitment_version, + }); Ok(()) } @@ -200,11 +192,9 @@ pub mod pallet { ) -> DispatchResult { let dispatcher = T::CommitOriginCheck::ensure_origin(origin, &identifier) .map(|e: ::CommitOrigin| e.submitter())?; - let commitment_version = version.unwrap_or(LATEST_COMMITMENT_VERSION); - let commitment = Self::delete_identity_commitment_storage_entry(&identifier, commitment_version)?; - T::ProviderHooks::on_commitment_removed(&identifier, &dispatcher, &commitment, commitment_version) - .map_err(|e| Error::::Hook(e.into()))?; + + Self::delete_identity_commitment_storage_entry(&identifier, &dispatcher, commitment_version)?; Ok(()) } } @@ -212,10 +202,13 @@ pub mod pallet { impl Pallet { pub fn delete_identity_commitment_storage_entry( identifier: &T::Identifier, + dispatcher: &T::AccountId, version: IdentityCommitmentVersion, - ) -> Result, DispatchError> { + ) -> Result, Error> { let commitment = IdentityCommitments::::take(identifier, version).ok_or(Error::::CommitmentNotFound)?; + T::ProviderHooks::on_commitment_removed(identifier, dispatcher, &commitment, version) + .map_err(|e| Error::::Hook(e.into()))?; Self::deposit_event(Event::::VersionedIdentityDeleted { identifier: identifier.clone(), version, diff --git a/runtimes/common/Cargo.toml b/runtimes/common/Cargo.toml index cfba19e88b..a330c99ada 100644 --- a/runtimes/common/Cargo.toml +++ b/runtimes/common/Cargo.toml @@ -24,7 +24,7 @@ smallvec.workspace = true attestation.workspace = true did.workspace = true kilt-support.workspace = true -kilt-dip-support.workspace = true +kilt-dip-primitives.workspace = true pallet-did-lookup.workspace = true pallet-dip-provider.workspace = true pallet-web3-names.workspace = true @@ -72,7 +72,7 @@ runtime-benchmarks = [ "attestation/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", - "kilt-dip-support/runtime-benchmarks", + "kilt-dip-primitives/runtime-benchmarks", "kilt-support/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-membership/runtime-benchmarks", @@ -98,7 +98,7 @@ std = [ "frame-system/std", "kilt-asset-dids/std", "kilt-support/std", - "kilt-dip-support/std", + "kilt-dip-primitives/std", "pallet-did-lookup/std", "pallet-dip-provider/std", "pallet-web3-names/std", diff --git a/runtimes/common/src/dip/did.rs b/runtimes/common/src/dip/did.rs index fbbda7a3f1..9056b7990d 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_support::merkle::RevealedWeb3Name; +use kilt_dip_primitives::merkle::RevealedWeb3Name; use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_dip_provider::traits::IdentityProvider; use parity_scale_codec::{Decode, Encode}; diff --git a/runtimes/common/src/dip/merkle.rs b/runtimes/common/src/dip/merkle.rs index 15e833a8f9..2ab8cb5bec 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_support::merkle::{DidKeyMerkleKey, DidKeyMerkleValue, DidMerkleProof}; +use kilt_dip_primitives::merkle::{DidKeyMerkleKey, DidKeyMerkleValue, DidMerkleProof}; use pallet_did_lookup::linkable_account::LinkableAccountId; use pallet_dip_provider::{ traits::{IdentityCommitmentGenerator, IdentityProvider}, @@ -29,7 +29,7 @@ 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_support::merkle::{DidKeyRelationship, RevealedDidMerkleProofLeaf}; +use kilt_dip_primitives::merkle::{DidKeyRelationship, RevealedDidMerkleProofLeaf}; use crate::dip::did::LinkedDidInfoOf; @@ -305,19 +305,18 @@ pub mod v0 { .collect::, _>>()?; match (should_include_web3_name, web3_name_details) { - // If web3name should be included and it exists... + // 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(), )); - Ok(()) } - // ...else if web3name should be included and it DOES NOT exist... - (true, None) => Err(DidMerkleProofError::Web3NameNotFound), - // ...else (if web3name should NOT be included). - (false, _) => Ok(()), - }?; + // ...else if web3name should be included and it DOES NOT exist, return an error... + (true, None) => return Err(DidMerkleProofError::Web3NameNotFound), + // ...else (if web3name should NOT be included), skip. + (false, _) => {} + }; let encoded_keys: Vec> = leaves.iter().map(|l| l.encoded_key()).collect(); let proof =