use crate::domain::entities::error::MemoError;
#[cfg(all(feature = "parity-scale-codec", feature = "scale-info"))]
use parity_scale_codec::{Decode, Encode};
#[cfg(all(feature = "parity-scale-codec", feature = "scale-info"))]
use scale_info::TypeInfo;
use super::mask::DisclosureMask;
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(
all(feature = "parity-scale-codec", feature = "scale-info"),
derive(Encode, Decode, TypeInfo)
)]
pub struct DisclosurePublicSignals {
pub commitment: [u8; 32],
pub revealed_value: u64,
pub revealed_asset_id: u32,
pub revealed_owner_hash: [u8; 32],
}
impl DisclosurePublicSignals {
pub fn new(
commitment: [u8; 32],
revealed_value: u64,
revealed_asset_id: u32,
revealed_owner_hash: [u8; 32],
) -> Self {
Self {
commitment,
revealed_value,
revealed_asset_id,
revealed_owner_hash,
}
}
pub fn to_bytes(&self) -> alloc::vec::Vec<u8> {
let mut bytes = alloc::vec::Vec::with_capacity(76);
bytes.extend_from_slice(&self.commitment);
bytes.extend_from_slice(&self.revealed_value.to_le_bytes());
bytes.extend_from_slice(&self.revealed_asset_id.to_le_bytes());
bytes.extend_from_slice(&self.revealed_owner_hash);
bytes
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, MemoError> {
if bytes.len() != 76 {
return Err(MemoError::InvalidProof("Invalid public signals length"));
}
let mut commitment = [0u8; 32];
commitment.copy_from_slice(&bytes[0..32]);
let revealed_value = u64::from_le_bytes(
bytes[32..40]
.try_into()
.map_err(|_| MemoError::InvalidProof("Invalid revealed_value"))?,
);
let revealed_asset_id = u32::from_le_bytes(
bytes[40..44]
.try_into()
.map_err(|_| MemoError::InvalidProof("Invalid revealed_asset_id"))?,
);
let mut revealed_owner_hash = [0u8; 32];
revealed_owner_hash.copy_from_slice(&bytes[44..76]);
Ok(Self {
commitment,
revealed_value,
revealed_asset_id,
revealed_owner_hash,
})
}
pub fn validate(&self, mask: &DisclosureMask) -> Result<(), MemoError> {
if !mask.disclose_owner && self.revealed_owner_hash != [0u8; 32] {
return Err(MemoError::InvalidProof(
"Owner hash must be zero when owner is not disclosed",
));
}
Ok(())
}
pub fn disclose_value(&self) -> bool {
self.revealed_value != 0
}
pub fn disclose_asset_id(&self) -> bool {
self.revealed_asset_id != 0
}
pub fn disclose_owner(&self) -> bool {
self.revealed_owner_hash != [0u8; 32]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_signals_new() {
let s = DisclosurePublicSignals::new([1u8; 32], 1000, 5, [2u8; 32]);
assert_eq!(s.commitment, [1u8; 32]);
assert_eq!(s.revealed_value, 1000);
assert_eq!(s.revealed_asset_id, 5);
assert_eq!(s.revealed_owner_hash, [2u8; 32]);
}
#[test]
fn test_signals_to_bytes_length() {
let s = DisclosurePublicSignals::new([1u8; 32], 500, 2, [3u8; 32]);
assert_eq!(s.to_bytes().len(), 76);
}
#[test]
fn test_signals_roundtrip() {
let original = DisclosurePublicSignals::new([10u8; 32], 1234, 56, [20u8; 32]);
let recovered = DisclosurePublicSignals::from_bytes(&original.to_bytes()).unwrap();
assert_eq!(recovered, original);
}
#[test]
fn test_signals_from_bytes_invalid_length() {
assert!(DisclosurePublicSignals::from_bytes(&[0u8; 50]).is_err());
}
#[test]
fn test_signals_validate_ok() {
let s = DisclosurePublicSignals::new([0u8; 32], 1000, 0, [0u8; 32]);
assert!(s.validate(&DisclosureMask::only_value()).is_ok());
}
#[test]
fn test_signals_validate_owner_hash_error() {
let s = DisclosurePublicSignals::new([0u8; 32], 1000, 0, [1u8; 32]);
assert!(s.validate(&DisclosureMask::only_value()).is_err());
}
#[test]
fn test_signals_helpers() {
let s1 = DisclosurePublicSignals::new([0u8; 32], 1000, 5, [1u8; 32]);
assert!(s1.disclose_value());
assert!(s1.disclose_asset_id());
assert!(s1.disclose_owner());
let s2 = DisclosurePublicSignals::new([0u8; 32], 0, 0, [0u8; 32]);
assert!(!s2.disclose_value());
assert!(!s2.disclose_asset_id());
assert!(!s2.disclose_owner());
}
#[test]
fn test_signals_clone() {
let s1 = DisclosurePublicSignals::new([5u8; 32], 999, 7, [10u8; 32]);
let s2 = s1.clone();
assert_eq!(s1, s2);
}
#[test]
fn test_signals_to_bytes_content() {
let commitment = [0xABu8; 32];
let value: u64 = 1234;
let asset_id: u32 = 56;
let owner_hash = [0xCDu8; 32];
let s = DisclosurePublicSignals::new(commitment, value, asset_id, owner_hash);
let bytes = s.to_bytes();
assert_eq!(&bytes[0..32], &commitment);
assert_eq!(&bytes[32..40], &value.to_le_bytes());
assert_eq!(&bytes[40..44], &asset_id.to_le_bytes());
assert_eq!(&bytes[44..76], &owner_hash);
}
#[test]
fn test_signals_validate_owner_disclosed_ok() {
use super::super::mask::DisclosureMask;
let s = DisclosurePublicSignals::new([0u8; 32], 0, 0, [1u8; 32]);
let mask = DisclosureMask::from_bitmap(0b0010); assert!(s.validate(&mask).is_ok());
}
}