use chia_protocol::Bytes32;
use chia_sdk_utils::Address;
use crate::error::{DidError, DidResult};
pub const DID_CHIA_PREFIX: &str = "did:chia:";
pub fn did_string_from_launcher_id(launcher_id: Bytes32) -> String {
Address::new(launcher_id, DID_CHIA_PREFIX.to_string())
.encode()
.expect("encoding a 32-byte launcher id under a fixed valid prefix never fails")
}
pub fn launcher_id_from_did_string(did: &str) -> DidResult<Bytes32> {
let candidate = did.trim();
let address = Address::decode(candidate)
.map_err(|error| DidError::InvalidDidString(error.to_string()))?;
if address.prefix != DID_CHIA_PREFIX {
return Err(DidError::InvalidDidString(format!(
"expected the '{DID_CHIA_PREFIX}' prefix, got '{}'",
address.prefix
)));
}
Ok(address.puzzle_hash)
}
#[cfg(test)]
mod tests {
use super::*;
fn sample_launcher_id() -> Bytes32 {
clvm_utils::tree_hash_atom(b"dig-did::did_string::sample-launcher-id").into()
}
#[test]
fn roundtrips_through_the_canonical_string_form() {
let launcher_id = sample_launcher_id();
let did = did_string_from_launcher_id(launcher_id);
assert!(did.starts_with(DID_CHIA_PREFIX));
let decoded = launcher_id_from_did_string(&did).expect("a freshly-encoded DID must decode");
assert_eq!(decoded, launcher_id);
}
#[test]
fn trims_surrounding_whitespace_before_decoding() {
let launcher_id = sample_launcher_id();
let did = did_string_from_launcher_id(launcher_id);
let padded = format!(" {did}\n");
let decoded =
launcher_id_from_did_string(&padded).expect("padding must not break decoding");
assert_eq!(decoded, launcher_id);
}
#[test]
fn rejects_malformed_bech32m() {
let error = launcher_id_from_did_string("not-a-valid-bech32m-string")
.expect_err("garbage input must fail closed");
assert!(matches!(error, DidError::InvalidDidString(_)));
}
#[test]
fn rejects_a_wrong_human_readable_prefix() {
let not_a_did = Address::new(sample_launcher_id(), "xch".to_string())
.encode()
.expect("a 32-byte payload under the 'xch' prefix encodes fine");
let error = launcher_id_from_did_string(¬_a_did)
.expect_err("a non-did:chia prefix must be rejected, never silently accepted");
assert!(matches!(error, DidError::InvalidDidString(_)));
}
#[test]
fn byte_agrees_with_the_chia_sdk_utils_address_codec() {
let launcher_id = sample_launcher_id();
let via_address = Address::new(launcher_id, DID_CHIA_PREFIX.to_string())
.encode()
.expect("encoding must succeed for a well-formed 32-byte payload");
let via_dig_did = did_string_from_launcher_id(launcher_id);
assert_eq!(via_address, via_dig_did);
}
}