use cid::multihash::Multihash;
use cowprotocol_primitives::AppDataHash;
pub type AppDataCid = cid::Cid;
pub(crate) const CID_CODEC_RAW: u64 = 0x55;
pub(crate) const MULTIHASH_KECCAK_256: u64 = 0x1b;
pub const MAX_CID_STR_LEN: usize = 128;
pub fn parse_app_data_cid(s: &str) -> Result<AppDataCid, AppDataCidError> {
if s.len() > MAX_CID_STR_LEN {
return Err(AppDataCidError::CidTooLong {
len: s.len(),
max: MAX_CID_STR_LEN,
});
}
Ok(s.parse::<AppDataCid>()?)
}
pub fn app_data_cid(hash: AppDataHash) -> AppDataCid {
let multihash = Multihash::<32>::wrap(MULTIHASH_KECCAK_256, hash.as_slice())
.expect("digest fits a 32-byte multihash by construction");
AppDataCid::new_v1(CID_CODEC_RAW, multihash.resize().expect("32 <= 64"))
}
pub fn app_data_hash_from_cid(cid: &AppDataCid) -> Result<AppDataHash, AppDataCidError> {
if cid.codec() != CID_CODEC_RAW {
return Err(AppDataCidError::UnexpectedCodec(cid.codec()));
}
let multihash = cid.hash();
if multihash.code() != MULTIHASH_KECCAK_256 {
return Err(AppDataCidError::UnexpectedMultihashCode(multihash.code()));
}
let digest = multihash.digest();
if digest.len() != 32 {
return Err(AppDataCidError::UnexpectedDigestLength(digest.len()));
}
Ok(AppDataHash::from_slice(digest))
}
#[derive(Debug, thiserror::Error)]
pub enum AppDataCidError {
#[error("invalid CID: {0}")]
InvalidCid(#[from] cid::Error),
#[error("CID string exceeds {max}-char cap (got {len})")]
CidTooLong {
len: usize,
max: usize,
},
#[error("expected raw codec (0x55), got 0x{0:02x}")]
UnexpectedCodec(u64),
#[error("expected keccak-256 multihash (0x1b), got 0x{0:02x}")]
UnexpectedMultihashCode(u64),
#[error("expected 32-byte digest, got {0}")]
UnexpectedDigestLength(usize),
}