use frame_support::ensure;
use pallet_dip_provider::IdentityCommitmentOf;
use parity_scale_codec::{Decode, Encode};
use scale_info::TypeInfo;
use sp_runtime::{
traits::{Hash, Header as HeaderT},
BoundedVec, SaturatedConversion,
};
use sp_std::{fmt::Debug, vec::Vec};
use sp_trie::{verify_trie_proof, LayoutV1};
use crate::{
merkle_proofs::v0::{
dip_subject_state::DipRevealedDetailsAndUnverifiedDidSignature,
input_common::{DidMerkleProof, DipCommitmentStateProof, ProviderHeadStateProof, TimeBoundDidSignature},
},
state_proofs::{verify_storage_value_proof, verify_storage_value_proof_with_decoder},
traits::GetWithArg,
utils::{
calculate_dip_identity_commitment_storage_key_for_runtime, calculate_parachain_head_storage_key, OutputOf,
},
Error,
};
#[cfg(test)]
mod tests;
#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo)]
pub struct ParachainDipDidProof<
RelayBlockNumber,
KiltDidKeyId,
KiltAccountId,
KiltBlockNumber,
KiltWeb3Name,
KiltLinkableAccountId,
ConsumerBlockNumber,
> {
pub(crate) provider_head_proof: ProviderHeadStateProof<RelayBlockNumber>,
pub(crate) dip_commitment_proof: DipCommitmentStateProof,
pub(crate) dip_proof:
DidMerkleProof<KiltDidKeyId, KiltAccountId, KiltBlockNumber, KiltWeb3Name, KiltLinkableAccountId>,
pub(crate) signature: TimeBoundDidSignature<ConsumerBlockNumber>,
}
impl<
RelayBlockNumber,
KiltDidKeyId,
KiltAccountId,
KiltBlockNumber,
KiltWeb3Name,
KiltLinkableAccountId,
ConsumerBlockNumber,
>
ParachainDipDidProof<
RelayBlockNumber,
KiltDidKeyId,
KiltAccountId,
KiltBlockNumber,
KiltWeb3Name,
KiltLinkableAccountId,
ConsumerBlockNumber,
>
{
pub fn new(
provider_head_proof: ProviderHeadStateProof<RelayBlockNumber>,
dip_commitment_proof: DipCommitmentStateProof,
dip_proof: DidMerkleProof<KiltDidKeyId, KiltAccountId, KiltBlockNumber, KiltWeb3Name, KiltLinkableAccountId>,
signature: TimeBoundDidSignature<ConsumerBlockNumber>,
) -> Self {
Self {
dip_commitment_proof,
dip_proof,
provider_head_proof,
signature,
}
}
pub fn provider_head_proof(&self) -> &ProviderHeadStateProof<RelayBlockNumber> {
&self.provider_head_proof
}
pub fn dip_commitment_proof(&self) -> &DipCommitmentStateProof {
&self.dip_commitment_proof
}
pub fn dip_proof(
&self,
) -> &DidMerkleProof<KiltDidKeyId, KiltAccountId, KiltBlockNumber, KiltWeb3Name, KiltLinkableAccountId> {
&self.dip_proof
}
pub fn signature(&self) -> &TimeBoundDidSignature<ConsumerBlockNumber> {
&self.signature
}
}
impl<
RelayBlockNumber,
KiltDidKeyId,
KiltAccountId,
KiltBlockNumber,
KiltWeb3Name,
KiltLinkableAccountId,
ConsumerBlockNumber,
>
ParachainDipDidProof<
RelayBlockNumber,
KiltDidKeyId,
KiltAccountId,
KiltBlockNumber,
KiltWeb3Name,
KiltLinkableAccountId,
ConsumerBlockNumber,
>
{
#[allow(clippy::type_complexity)]
pub fn verify_provider_head_proof_with_state_root<RelayHasher, ProviderHeader>(
self,
provider_para_id: u32,
relay_state_root: &OutputOf<RelayHasher>,
) -> Result<
DipDidProofWithVerifiedStateRoot<
OutputOf<RelayHasher>,
KiltDidKeyId,
KiltAccountId,
KiltBlockNumber,
KiltWeb3Name,
KiltLinkableAccountId,
ConsumerBlockNumber,
>,
Error,
>
where
RelayHasher: Hash,
ProviderHeader: Decode + HeaderT<Hash = OutputOf<RelayHasher>, Number = KiltBlockNumber>,
{
let provider_head_storage_key = calculate_parachain_head_storage_key(provider_para_id);
log::trace!(target: "dip::consumer::ParachainDipDidProofV0", "Calculated storage key for para ID {:#?} = {:#?}", provider_para_id, provider_head_storage_key);
let provider_header = verify_storage_value_proof_with_decoder::<_, RelayHasher, ProviderHeader>(
&provider_head_storage_key,
*relay_state_root,
self.provider_head_proof.proof,
|input| {
if input.len() < 2 {
return None;
}
let mut trimmed_input = &input[2..];
ProviderHeader::decode(&mut trimmed_input).ok()
},
)
.map_err(Error::ParaHeadMerkleProof)?;
Ok(DipDidProofWithVerifiedStateRoot {
state_root: *provider_header.state_root(),
dip_commitment_proof: self.dip_commitment_proof,
dip_proof: self.dip_proof,
signature: self.signature,
})
}
#[allow(clippy::type_complexity)]
pub fn verify_provider_head_proof<RelayHasher, StateRootStore, ProviderHeader>(
self,
provider_para_id: u32,
) -> Result<
DipDidProofWithVerifiedStateRoot<
OutputOf<RelayHasher>,
KiltDidKeyId,
KiltAccountId,
KiltBlockNumber,
KiltWeb3Name,
KiltLinkableAccountId,
ConsumerBlockNumber,
>,
Error,
>
where
RelayHasher: Hash,
StateRootStore: GetWithArg<RelayBlockNumber, Result = Option<OutputOf<RelayHasher>>>,
ProviderHeader: Decode + HeaderT<Hash = OutputOf<RelayHasher>, Number = KiltBlockNumber>,
{
let relay_state_root =
StateRootStore::get(&self.provider_head_proof.relay_block_number).ok_or(Error::RelayStateRootNotFound)?;
self.verify_provider_head_proof_with_state_root::<RelayHasher, ProviderHeader>(
provider_para_id,
&relay_state_root,
)
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct DipDidProofWithVerifiedStateRoot<
StateRoot,
KiltDidKeyId,
KiltAccountId,
KiltBlockNumber,
KiltWeb3Name,
KiltLinkableAccountId,
ConsumerBlockNumber,
> {
pub(crate) state_root: StateRoot,
pub(crate) dip_commitment_proof: DipCommitmentStateProof,
pub(crate) dip_proof:
DidMerkleProof<KiltDidKeyId, KiltAccountId, KiltBlockNumber, KiltWeb3Name, KiltLinkableAccountId>,
pub(crate) signature: TimeBoundDidSignature<ConsumerBlockNumber>,
}
impl<
StateRoot,
KiltDidKeyId,
KiltAccountId,
KiltBlockNumber,
KiltWeb3Name,
KiltLinkableAccountId,
ConsumerBlockNumber,
>
DipDidProofWithVerifiedStateRoot<
StateRoot,
KiltDidKeyId,
KiltAccountId,
KiltBlockNumber,
KiltWeb3Name,
KiltLinkableAccountId,
ConsumerBlockNumber,
>
{
#[allow(clippy::type_complexity)]
pub fn verify_dip_commitment_proof_for_subject<ParachainHasher, ProviderRuntime>(
self,
subject: &ProviderRuntime::Identifier,
) -> Result<
DipDidProofWithVerifiedSubjectCommitment<
IdentityCommitmentOf<ProviderRuntime>,
KiltDidKeyId,
KiltAccountId,
KiltBlockNumber,
KiltWeb3Name,
KiltLinkableAccountId,
ConsumerBlockNumber,
>,
Error,
>
where
StateRoot: Ord,
ParachainHasher: Hash<Output = StateRoot>,
ProviderRuntime: pallet_dip_provider::Config,
{
let dip_commitment_storage_key =
calculate_dip_identity_commitment_storage_key_for_runtime::<ProviderRuntime>(subject, 0);
log::trace!(target: "dip::consumer::DipDidProofWithVerifiedStateRootV0", "Calculated storage key for subject {:#?} = {:#?}", subject, dip_commitment_storage_key);
let dip_commitment = verify_storage_value_proof::<_, ParachainHasher, IdentityCommitmentOf<ProviderRuntime>>(
&dip_commitment_storage_key,
self.state_root,
self.dip_commitment_proof.0,
)
.map_err(Error::DipCommitmentMerkleProof)?;
Ok(DipDidProofWithVerifiedSubjectCommitment {
dip_commitment,
dip_proof: self.dip_proof,
signature: self.signature,
})
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct DipDidProofWithVerifiedSubjectCommitment<
Commitment,
KiltDidKeyId,
KiltAccountId,
KiltBlockNumber,
KiltWeb3Name,
KiltLinkableAccountId,
ConsumerBlockNumber,
> {
pub(crate) dip_commitment: Commitment,
pub(crate) dip_proof:
DidMerkleProof<KiltDidKeyId, KiltAccountId, KiltBlockNumber, KiltWeb3Name, KiltLinkableAccountId>,
pub(crate) signature: TimeBoundDidSignature<ConsumerBlockNumber>,
}
impl<
Commitment,
KiltDidKeyId,
KiltAccountId,
KiltBlockNumber,
KiltWeb3Name,
KiltLinkableAccountId,
ConsumerBlockNumber,
>
DipDidProofWithVerifiedSubjectCommitment<
Commitment,
KiltDidKeyId,
KiltAccountId,
KiltBlockNumber,
KiltWeb3Name,
KiltLinkableAccountId,
ConsumerBlockNumber,
>
{
pub fn new(
dip_commitment: Commitment,
dip_proof: DidMerkleProof<KiltDidKeyId, KiltAccountId, KiltBlockNumber, KiltWeb3Name, KiltLinkableAccountId>,
signature: TimeBoundDidSignature<ConsumerBlockNumber>,
) -> Self {
Self {
dip_commitment,
dip_proof,
signature,
}
}
}
impl<
Commitment,
KiltDidKeyId,
KiltAccountId,
KiltBlockNumber,
KiltWeb3Name,
KiltLinkableAccountId,
ConsumerBlockNumber,
>
DipDidProofWithVerifiedSubjectCommitment<
Commitment,
KiltDidKeyId,
KiltAccountId,
KiltBlockNumber,
KiltWeb3Name,
KiltLinkableAccountId,
ConsumerBlockNumber,
> where
KiltDidKeyId: Encode,
KiltAccountId: Encode,
KiltBlockNumber: Encode,
KiltWeb3Name: Encode,
KiltLinkableAccountId: Encode,
{
pub fn verify_dip_proof<DidMerkleHasher, const MAX_REVEALED_LEAVES_COUNT: u32>(
self,
) -> Result<
DipRevealedDetailsAndUnverifiedDidSignature<
KiltDidKeyId,
KiltAccountId,
KiltBlockNumber,
KiltWeb3Name,
KiltLinkableAccountId,
ConsumerBlockNumber,
MAX_REVEALED_LEAVES_COUNT,
>,
Error,
>
where
DidMerkleHasher: Hash<Output = Commitment>,
{
ensure!(
self.dip_proof.revealed.len() <= MAX_REVEALED_LEAVES_COUNT.saturated_into(),
Error::TooManyLeavesRevealed
);
let proof_leaves_key_value_pairs = self
.dip_proof
.revealed
.iter()
.map(|revealed_leaf| (revealed_leaf.encoded_key(), Some(revealed_leaf.encoded_value())))
.collect::<Vec<_>>();
verify_trie_proof::<LayoutV1<DidMerkleHasher>, _, _, _>(
&self.dip_commitment,
self.dip_proof.blinded.as_slice(),
proof_leaves_key_value_pairs.as_slice(),
)
.map_err(|_| Error::InvalidDidMerkleProof)?;
let revealed_leaves = BoundedVec::try_from(self.dip_proof.revealed).map_err(|_| {
log::error!(target: "dip::consumer::DipDidProofWithVerifiedSubjectCommitmentV0", "Failed to construct BoundedVec<u8, {MAX_REVEALED_LEAVES_COUNT}>.");
Error::Internal
})?;
Ok(DipRevealedDetailsAndUnverifiedDidSignature {
revealed_leaves,
signature: self.signature,
})
}
}