use alloc::collections::BTreeSet;
use alloc::string::String;
use alloc::vec::Vec;
use serde::{Deserialize, Serialize};
use crate::error::CompositionError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum HashAlgTag {
#[serde(rename = "poseidon-felt252")]
PoseidonFelt252,
#[serde(rename = "poseidon-mersenne31")]
PoseidonMersenne31,
#[serde(rename = "sha-256")]
Sha256,
}
impl Default for HashAlgTag {
fn default() -> Self {
Self::PoseidonFelt252
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CommittedField {
pub path: String,
#[serde(with = "serde_bytes")]
pub salt: Vec<u8>,
#[serde(with = "serde_bytes")]
pub digest: Vec<u8>,
}
impl CommittedField {
pub fn validate_shape(&self) -> Result<(), CompositionError> {
if self.salt.len() != 32 {
return Err(CompositionError::Invariant(
"committed-field.salt must be 32 bytes",
));
}
if self.digest.len() != 32 {
return Err(CompositionError::Invariant(
"committed-field.digest must be 32 bytes",
));
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RevelationMask {
pub disclosed: Vec<String>,
pub committed: Vec<CommittedField>,
#[serde(rename = "policy-uri", default, skip_serializing_if = "Option::is_none")]
pub policy_uri: Option<String>,
#[serde(rename = "hash-alg", default, skip_serializing_if = "Option::is_none")]
pub hash_alg: Option<HashAlgTag>,
}
impl RevelationMask {
pub fn new(
disclosed: Vec<String>,
committed: Vec<CommittedField>,
hash_alg: Option<HashAlgTag>,
) -> Result<Self, CompositionError> {
let m = Self {
disclosed,
committed,
policy_uri: None,
hash_alg,
};
m.validate_shape()?;
Ok(m)
}
pub fn validate_shape(&self) -> Result<(), CompositionError> {
for cf in &self.committed {
cf.validate_shape()?;
}
let disclosed: BTreeSet<&String> = self.disclosed.iter().collect();
for cf in &self.committed {
if disclosed.contains(&cf.path) {
return Err(CompositionError::MaskDisjointnessViolation);
}
}
Ok(())
}
pub fn disclosed(&self) -> &[String] { &self.disclosed }
pub fn committed_paths(&self) -> Vec<&str> {
self.committed.iter().map(|cf| cf.path.as_str()).collect()
}
pub fn refines(&self, other: &Self) -> bool {
let s: BTreeSet<&String> = self.disclosed.iter().collect();
let o: BTreeSet<&String> = other.disclosed.iter().collect();
s.is_subset(&o)
}
}