use alloc::string::{String, ToString};
use alloc::vec::Vec;
use bech32::{Bech32, Hrp};
use super::bech32 as prefixes;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Bech32Error {
InvalidHrp(String),
InvalidEncoding(String),
InvalidData(String),
ChecksumError,
UnexpectedLength {
expected: usize,
actual: usize,
},
HrpMismatch {
expected: String,
actual: String,
},
}
impl core::fmt::Display for Bech32Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::InvalidHrp(s) => write!(f, "Invalid human-readable part: {}", s),
Self::InvalidEncoding(s) => write!(f, "Invalid Bech32 encoding: {}", s),
Self::InvalidData(s) => write!(f, "Invalid data: {}", s),
Self::ChecksumError => write!(f, "Bech32 checksum verification failed"),
Self::UnexpectedLength { expected, actual } => {
write!(
f,
"Unexpected data length: expected {} bytes, got {}",
expected, actual
)
}
Self::HrpMismatch { expected, actual } => {
write!(f, "HRP mismatch: expected '{}', got '{}'", expected, actual)
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Bech32Error {}
pub type Bech32Result<T> = core::result::Result<T, Bech32Error>;
#[inline]
pub fn encode_to_bech32(hrp: &str, data: &[u8]) -> Bech32Result<String> {
let hrp = Hrp::parse(hrp).map_err(|e| Bech32Error::InvalidHrp(e.to_string()))?;
bech32::encode::<Bech32>(hrp, data).map_err(|e| Bech32Error::InvalidEncoding(e.to_string()))
}
#[inline]
pub fn decode_from_bech32(encoded: &str) -> Bech32Result<(String, Vec<u8>)> {
let (hrp, data) =
bech32::decode(encoded).map_err(|e| Bech32Error::InvalidEncoding(e.to_string()))?;
Ok((hrp.to_string(), data))
}
#[inline]
pub fn decode_with_hrp(encoded: &str, expected_hrp: &str) -> Bech32Result<Vec<u8>> {
let (hrp, data) = decode_from_bech32(encoded)?;
if hrp != expected_hrp {
return Err(Bech32Error::HrpMismatch {
expected: expected_hrp.to_string(),
actual: hrp,
});
}
Ok(data)
}
#[inline]
pub fn decode_to_array<const N: usize>(encoded: &str, expected_hrp: &str) -> Bech32Result<[u8; N]> {
let data = decode_with_hrp(encoded, expected_hrp)?;
if data.len() != N {
return Err(Bech32Error::UnexpectedLength {
expected: N,
actual: data.len(),
});
}
let mut result = [0u8; N];
result.copy_from_slice(&data);
Ok(result)
}
pub fn encode_payment_verification_key(vk: &[u8; 32]) -> Bech32Result<String> {
encode_to_bech32(prefixes::PAYMENT_VERIFICATION_KEY_PREFIX, vk)
}
pub fn decode_payment_verification_key(encoded: &str) -> Bech32Result<[u8; 32]> {
decode_to_array(encoded, prefixes::PAYMENT_VERIFICATION_KEY_PREFIX)
}
pub fn encode_payment_signing_key(sk: &[u8; 32]) -> Bech32Result<String> {
encode_to_bech32(prefixes::PAYMENT_SIGNING_KEY_PREFIX, sk)
}
pub fn decode_payment_signing_key(encoded: &str) -> Bech32Result<[u8; 32]> {
decode_to_array(encoded, prefixes::PAYMENT_SIGNING_KEY_PREFIX)
}
pub fn encode_stake_verification_key(vk: &[u8; 32]) -> Bech32Result<String> {
encode_to_bech32(prefixes::STAKE_VERIFICATION_KEY_PREFIX, vk)
}
pub fn decode_stake_verification_key(encoded: &str) -> Bech32Result<[u8; 32]> {
decode_to_array(encoded, prefixes::STAKE_VERIFICATION_KEY_PREFIX)
}
pub fn encode_stake_signing_key(sk: &[u8; 32]) -> Bech32Result<String> {
encode_to_bech32(prefixes::STAKE_SIGNING_KEY_PREFIX, sk)
}
pub fn decode_stake_signing_key(encoded: &str) -> Bech32Result<[u8; 32]> {
decode_to_array(encoded, prefixes::STAKE_SIGNING_KEY_PREFIX)
}
pub fn encode_pool_verification_key(vk: &[u8; 32]) -> Bech32Result<String> {
encode_to_bech32(prefixes::POOL_VERIFICATION_KEY_PREFIX, vk)
}
pub fn decode_pool_verification_key(encoded: &str) -> Bech32Result<[u8; 32]> {
decode_to_array(encoded, prefixes::POOL_VERIFICATION_KEY_PREFIX)
}
pub fn encode_pool_signing_key(sk: &[u8; 32]) -> Bech32Result<String> {
encode_to_bech32(prefixes::POOL_SIGNING_KEY_PREFIX, sk)
}
pub fn decode_pool_signing_key(encoded: &str) -> Bech32Result<[u8; 32]> {
decode_to_array(encoded, prefixes::POOL_SIGNING_KEY_PREFIX)
}
pub fn encode_vrf_verification_key(vk: &[u8; 32]) -> Bech32Result<String> {
encode_to_bech32(prefixes::VRF_VERIFICATION_KEY_PREFIX, vk)
}
pub fn decode_vrf_verification_key(encoded: &str) -> Bech32Result<[u8; 32]> {
decode_to_array(encoded, prefixes::VRF_VERIFICATION_KEY_PREFIX)
}
pub fn encode_vrf_signing_key(sk: &[u8; 32]) -> Bech32Result<String> {
encode_to_bech32(prefixes::VRF_SIGNING_KEY_PREFIX, sk)
}
pub fn decode_vrf_signing_key(encoded: &str) -> Bech32Result<[u8; 32]> {
decode_to_array(encoded, prefixes::VRF_SIGNING_KEY_PREFIX)
}
pub fn encode_vrf_signing_key_full(sk: &[u8; 64]) -> Bech32Result<String> {
encode_to_bech32(prefixes::VRF_SIGNING_KEY_PREFIX, sk)
}
pub fn decode_vrf_signing_key_full(encoded: &str) -> Bech32Result<[u8; 64]> {
decode_to_array(encoded, prefixes::VRF_SIGNING_KEY_PREFIX)
}
pub fn encode_kes_verification_key(vk: &[u8; 32]) -> Bech32Result<String> {
encode_to_bech32(prefixes::KES_VERIFICATION_KEY_PREFIX, vk)
}
pub fn decode_kes_verification_key(encoded: &str) -> Bech32Result<[u8; 32]> {
decode_to_array(encoded, prefixes::KES_VERIFICATION_KEY_PREFIX)
}
pub fn encode_kes_signing_key(sk: &[u8]) -> Bech32Result<String> {
encode_to_bech32(prefixes::KES_SIGNING_KEY_PREFIX, sk)
}
pub fn decode_kes_signing_key(encoded: &str) -> Bech32Result<Vec<u8>> {
decode_with_hrp(encoded, prefixes::KES_SIGNING_KEY_PREFIX)
}
pub fn encode_genesis_verification_key(vk: &[u8; 32]) -> Bech32Result<String> {
encode_to_bech32(prefixes::GENESIS_VERIFICATION_KEY_PREFIX, vk)
}
pub fn decode_genesis_verification_key(encoded: &str) -> Bech32Result<[u8; 32]> {
decode_to_array(encoded, prefixes::GENESIS_VERIFICATION_KEY_PREFIX)
}
pub fn encode_genesis_signing_key(sk: &[u8; 32]) -> Bech32Result<String> {
encode_to_bech32(prefixes::GENESIS_SIGNING_KEY_PREFIX, sk)
}
pub fn decode_genesis_signing_key(encoded: &str) -> Bech32Result<[u8; 32]> {
decode_to_array(encoded, prefixes::GENESIS_SIGNING_KEY_PREFIX)
}
pub fn encode_genesis_delegate_verification_key(vk: &[u8; 32]) -> Bech32Result<String> {
encode_to_bech32(prefixes::GENESIS_DELEGATE_VERIFICATION_KEY_PREFIX, vk)
}
pub fn decode_genesis_delegate_verification_key(encoded: &str) -> Bech32Result<[u8; 32]> {
decode_to_array(encoded, prefixes::GENESIS_DELEGATE_VERIFICATION_KEY_PREFIX)
}
pub fn encode_genesis_delegate_signing_key(sk: &[u8; 32]) -> Bech32Result<String> {
encode_to_bech32(prefixes::GENESIS_DELEGATE_SIGNING_KEY_PREFIX, sk)
}
pub fn decode_genesis_delegate_signing_key(encoded: &str) -> Bech32Result<[u8; 32]> {
decode_to_array(encoded, prefixes::GENESIS_DELEGATE_SIGNING_KEY_PREFIX)
}
pub fn encode_drep_verification_key(vk: &[u8; 32]) -> Bech32Result<String> {
encode_to_bech32(prefixes::DREP_VERIFICATION_KEY_PREFIX, vk)
}
pub fn decode_drep_verification_key(encoded: &str) -> Bech32Result<[u8; 32]> {
decode_to_array(encoded, prefixes::DREP_VERIFICATION_KEY_PREFIX)
}
pub fn encode_drep_signing_key(sk: &[u8; 32]) -> Bech32Result<String> {
encode_to_bech32(prefixes::DREP_SIGNING_KEY_PREFIX, sk)
}
pub fn decode_drep_signing_key(encoded: &str) -> Bech32Result<[u8; 32]> {
decode_to_array(encoded, prefixes::DREP_SIGNING_KEY_PREFIX)
}
pub fn encode_committee_cold_verification_key(vk: &[u8; 32]) -> Bech32Result<String> {
encode_to_bech32(prefixes::COMMITTEE_COLD_VERIFICATION_KEY_PREFIX, vk)
}
pub fn decode_committee_cold_verification_key(encoded: &str) -> Bech32Result<[u8; 32]> {
decode_to_array(encoded, prefixes::COMMITTEE_COLD_VERIFICATION_KEY_PREFIX)
}
pub fn encode_committee_cold_signing_key(sk: &[u8; 32]) -> Bech32Result<String> {
encode_to_bech32(prefixes::COMMITTEE_COLD_SIGNING_KEY_PREFIX, sk)
}
pub fn decode_committee_cold_signing_key(encoded: &str) -> Bech32Result<[u8; 32]> {
decode_to_array(encoded, prefixes::COMMITTEE_COLD_SIGNING_KEY_PREFIX)
}
pub fn encode_committee_hot_verification_key(vk: &[u8; 32]) -> Bech32Result<String> {
encode_to_bech32(prefixes::COMMITTEE_HOT_VERIFICATION_KEY_PREFIX, vk)
}
pub fn decode_committee_hot_verification_key(encoded: &str) -> Bech32Result<[u8; 32]> {
decode_to_array(encoded, prefixes::COMMITTEE_HOT_VERIFICATION_KEY_PREFIX)
}
pub fn encode_committee_hot_signing_key(sk: &[u8; 32]) -> Bech32Result<String> {
encode_to_bech32(prefixes::COMMITTEE_HOT_SIGNING_KEY_PREFIX, sk)
}
pub fn decode_committee_hot_signing_key(encoded: &str) -> Bech32Result<[u8; 32]> {
decode_to_array(encoded, prefixes::COMMITTEE_HOT_SIGNING_KEY_PREFIX)
}
pub fn encode_pool_id(hash: &[u8; 28]) -> Bech32Result<String> {
encode_to_bech32(prefixes::POOL_HASH_PREFIX, hash)
}
pub fn decode_pool_id(encoded: &str) -> Bech32Result<[u8; 28]> {
decode_to_array(encoded, prefixes::POOL_HASH_PREFIX)
}
pub fn encode_verification_key_hash(hash: &[u8; 28]) -> Bech32Result<String> {
encode_to_bech32(prefixes::VERIFICATION_KEY_HASH_PREFIX, hash)
}
pub fn decode_verification_key_hash(encoded: &str) -> Bech32Result<[u8; 28]> {
decode_to_array(encoded, prefixes::VERIFICATION_KEY_HASH_PREFIX)
}
pub fn encode_stake_verification_key_hash(hash: &[u8; 28]) -> Bech32Result<String> {
encode_to_bech32(prefixes::STAKE_VERIFICATION_KEY_HASH_PREFIX, hash)
}
pub fn decode_stake_verification_key_hash(encoded: &str) -> Bech32Result<[u8; 28]> {
decode_to_array(encoded, prefixes::STAKE_VERIFICATION_KEY_HASH_PREFIX)
}
pub fn encode_vrf_verification_key_hash(hash: &[u8; 28]) -> Bech32Result<String> {
encode_to_bech32(prefixes::VRF_VERIFICATION_KEY_HASH_PREFIX, hash)
}
pub fn decode_vrf_verification_key_hash(encoded: &str) -> Bech32Result<[u8; 28]> {
decode_to_array(encoded, prefixes::VRF_VERIFICATION_KEY_HASH_PREFIX)
}
pub fn encode_script_hash(hash: &[u8; 28]) -> Bech32Result<String> {
encode_to_bech32(prefixes::SCRIPT_HASH_PREFIX, hash)
}
pub fn decode_script_hash(encoded: &str) -> Bech32Result<[u8; 28]> {
decode_to_array(encoded, prefixes::SCRIPT_HASH_PREFIX)
}
pub fn encode_drep_key_hash(hash: &[u8; 28]) -> Bech32Result<String> {
encode_to_bech32(prefixes::DREP_KEY_HASH_PREFIX, hash)
}
pub fn decode_drep_key_hash(encoded: &str) -> Bech32Result<[u8; 28]> {
decode_to_array(encoded, prefixes::DREP_KEY_HASH_PREFIX)
}
pub fn is_valid_bech32(s: &str) -> bool {
bech32::decode(s).is_ok()
}
pub fn get_hrp(s: &str) -> Option<String> {
bech32::decode(s).ok().map(|(hrp, _)| hrp.to_string())
}
pub const SEPARATOR_CHAR: char = '1';
pub const ENCODED_STRING_MAX_LENGTH: usize = 90;
pub const ENCODED_STRING_MIN_LENGTH: usize = 8;
pub const CHECKSUM_LENGTH: usize = 6;
pub const HRP_MIN_LENGTH: usize = 1;
pub const HRP_MAX_LENGTH: usize = 83;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_decode_roundtrip() {
let data = [1u8; 32];
let encoded = encode_to_bech32("test", &data).unwrap();
let (hrp, decoded) = decode_from_bech32(&encoded).unwrap();
assert_eq!(hrp, "test");
assert_eq!(decoded, data);
}
#[test]
fn test_payment_key_roundtrip() {
let vk = [42u8; 32];
let encoded = encode_payment_verification_key(&vk).unwrap();
assert!(encoded.starts_with("addr_vk1"));
let decoded = decode_payment_verification_key(&encoded).unwrap();
assert_eq!(decoded, vk);
}
#[test]
fn test_stake_key_roundtrip() {
let vk = [42u8; 32];
let encoded = encode_stake_verification_key(&vk).unwrap();
assert!(encoded.starts_with("stake_vk1"));
let decoded = decode_stake_verification_key(&encoded).unwrap();
assert_eq!(decoded, vk);
}
#[test]
fn test_pool_key_roundtrip() {
let vk = [42u8; 32];
let encoded = encode_pool_verification_key(&vk).unwrap();
assert!(encoded.starts_with("pool_vk1"));
let decoded = decode_pool_verification_key(&encoded).unwrap();
assert_eq!(decoded, vk);
}
#[test]
fn test_vrf_key_roundtrip() {
let vk = [42u8; 32];
let encoded = encode_vrf_verification_key(&vk).unwrap();
assert!(encoded.starts_with("vrf_vk1"));
let decoded = decode_vrf_verification_key(&encoded).unwrap();
assert_eq!(decoded, vk);
}
#[test]
fn test_kes_key_roundtrip() {
let vk = [42u8; 32];
let encoded = encode_kes_verification_key(&vk).unwrap();
assert!(encoded.starts_with("kes_vk1"));
let decoded = decode_kes_verification_key(&encoded).unwrap();
assert_eq!(decoded, vk);
}
#[test]
fn test_pool_id_roundtrip() {
let hash = [42u8; 28];
let encoded = encode_pool_id(&hash).unwrap();
assert!(encoded.starts_with("pool1"));
let decoded = decode_pool_id(&encoded).unwrap();
assert_eq!(decoded, hash);
}
#[test]
fn test_decode_with_wrong_hrp() {
let vk = [42u8; 32];
let encoded = encode_vrf_verification_key(&vk).unwrap();
let result = decode_with_hrp(&encoded, "kes_vk");
assert!(matches!(result, Err(Bech32Error::HrpMismatch { .. })));
}
#[test]
fn test_decode_invalid_string() {
let result = decode_from_bech32("invalid");
assert!(result.is_err());
}
#[test]
fn test_is_valid_bech32() {
let vk = [0u8; 32];
let encoded = encode_vrf_verification_key(&vk).unwrap();
assert!(is_valid_bech32(&encoded));
assert!(!is_valid_bech32("invalid"));
}
#[test]
fn test_get_hrp() {
let vk = [0u8; 32];
let encoded = encode_vrf_verification_key(&vk).unwrap();
assert_eq!(get_hrp(&encoded), Some("vrf_vk".to_string()));
assert_eq!(get_hrp("invalid"), None);
}
#[test]
fn test_constants() {
assert_eq!(SEPARATOR_CHAR, '1');
assert_eq!(ENCODED_STRING_MAX_LENGTH, 90);
assert_eq!(CHECKSUM_LENGTH, 6);
assert_eq!(HRP_MIN_LENGTH, 1);
assert_eq!(HRP_MAX_LENGTH, 83);
}
#[test]
fn test_governance_keys() {
let vk = [42u8; 32];
let drep_encoded = encode_drep_verification_key(&vk).unwrap();
assert!(drep_encoded.starts_with("drep_vk1"));
let drep_decoded = decode_drep_verification_key(&drep_encoded).unwrap();
assert_eq!(drep_decoded, vk);
let cc_cold_encoded = encode_committee_cold_verification_key(&vk).unwrap();
assert!(cc_cold_encoded.starts_with("cc_cold_vk1"));
let cc_cold_decoded = decode_committee_cold_verification_key(&cc_cold_encoded).unwrap();
assert_eq!(cc_cold_decoded, vk);
let cc_hot_encoded = encode_committee_hot_verification_key(&vk).unwrap();
assert!(cc_hot_encoded.starts_with("cc_hot_vk1"));
let cc_hot_decoded = decode_committee_hot_verification_key(&cc_hot_encoded).unwrap();
assert_eq!(cc_hot_decoded, vk);
}
#[test]
fn test_key_hash_encoding() {
let hash = [42u8; 28];
let vkh_encoded = encode_verification_key_hash(&hash).unwrap();
assert!(vkh_encoded.starts_with("addr_vkh1"));
let vkh_decoded = decode_verification_key_hash(&vkh_encoded).unwrap();
assert_eq!(vkh_decoded, hash);
let stake_vkh_encoded = encode_stake_verification_key_hash(&hash).unwrap();
assert!(stake_vkh_encoded.starts_with("stake_vkh1"));
let stake_vkh_decoded = decode_stake_verification_key_hash(&stake_vkh_encoded).unwrap();
assert_eq!(stake_vkh_decoded, hash);
let vrf_vkh_encoded = encode_vrf_verification_key_hash(&hash).unwrap();
assert!(vrf_vkh_encoded.starts_with("vrf_vkh1"));
let vrf_vkh_decoded = decode_vrf_verification_key_hash(&vrf_vkh_encoded).unwrap();
assert_eq!(vrf_vkh_decoded, hash);
let script_encoded = encode_script_hash(&hash).unwrap();
assert!(script_encoded.starts_with("script1"));
let script_decoded = decode_script_hash(&script_encoded).unwrap();
assert_eq!(script_decoded, hash);
}
#[test]
fn test_kes_signing_key_variable_length() {
let sk_small = vec![42u8; 100];
let encoded = encode_kes_signing_key(&sk_small).unwrap();
assert!(encoded.starts_with("kes_sk1"));
let decoded = decode_kes_signing_key(&encoded).unwrap();
assert_eq!(decoded, sk_small);
let sk_medium = vec![42u8; 500];
let encoded_medium = encode_kes_signing_key(&sk_medium).unwrap();
let decoded_medium = decode_kes_signing_key(&encoded_medium).unwrap();
assert_eq!(decoded_medium, sk_medium);
}
#[test]
fn test_signing_keys() {
let sk = [42u8; 32];
let payment_sk = encode_payment_signing_key(&sk).unwrap();
assert!(payment_sk.starts_with("addr_sk1"));
assert_eq!(decode_payment_signing_key(&payment_sk).unwrap(), sk);
let stake_sk = encode_stake_signing_key(&sk).unwrap();
assert!(stake_sk.starts_with("stake_sk1"));
assert_eq!(decode_stake_signing_key(&stake_sk).unwrap(), sk);
let pool_sk = encode_pool_signing_key(&sk).unwrap();
assert!(pool_sk.starts_with("pool_sk1"));
assert_eq!(decode_pool_signing_key(&pool_sk).unwrap(), sk);
let vrf_sk = encode_vrf_signing_key(&sk).unwrap();
assert!(vrf_sk.starts_with("vrf_sk1"));
assert_eq!(decode_vrf_signing_key(&vrf_sk).unwrap(), sk);
}
#[test]
fn test_vrf_full_signing_key() {
let sk = [42u8; 64];
let encoded = encode_vrf_signing_key_full(&sk).unwrap();
assert!(encoded.starts_with("vrf_sk1"));
let decoded = decode_vrf_signing_key_full(&encoded).unwrap();
assert_eq!(decoded, sk);
}
#[test]
fn test_genesis_keys() {
let key = [42u8; 32];
let genesis_vk = encode_genesis_verification_key(&key).unwrap();
assert!(genesis_vk.starts_with("genesis_vk1"));
assert_eq!(decode_genesis_verification_key(&genesis_vk).unwrap(), key);
let genesis_sk = encode_genesis_signing_key(&key).unwrap();
assert!(genesis_sk.starts_with("genesis_sk1"));
assert_eq!(decode_genesis_signing_key(&genesis_sk).unwrap(), key);
let gen_del_vk = encode_genesis_delegate_verification_key(&key).unwrap();
assert!(gen_del_vk.starts_with("genesis_delegate_vk1"));
assert_eq!(
decode_genesis_delegate_verification_key(&gen_del_vk).unwrap(),
key
);
let gen_del_sk = encode_genesis_delegate_signing_key(&key).unwrap();
assert!(gen_del_sk.starts_with("genesis_delegate_sk1"));
assert_eq!(
decode_genesis_delegate_signing_key(&gen_del_sk).unwrap(),
key
);
}
#[test]
fn test_drep_key_hash() {
let hash = [42u8; 28];
let encoded = encode_drep_key_hash(&hash).unwrap();
assert!(encoded.starts_with("drep1"));
let decoded = decode_drep_key_hash(&encoded).unwrap();
assert_eq!(decoded, hash);
}
#[test]
fn test_error_display() {
let err = Bech32Error::InvalidHrp("test".to_string());
assert!(err.to_string().contains("Invalid human-readable part"));
let err = Bech32Error::HrpMismatch {
expected: "vrf_vk".to_string(),
actual: "kes_vk".to_string(),
};
assert!(err.to_string().contains("HRP mismatch"));
let err = Bech32Error::UnexpectedLength {
expected: 32,
actual: 28,
};
assert!(err.to_string().contains("Unexpected data length"));
}
#[test]
fn test_zero_key() {
let zero_key = [0u8; 32];
let encoded = encode_vrf_verification_key(&zero_key).unwrap();
let decoded = decode_vrf_verification_key(&encoded).unwrap();
assert_eq!(decoded, zero_key);
}
#[test]
fn test_max_key() {
let max_key = [255u8; 32];
let encoded = encode_vrf_verification_key(&max_key).unwrap();
let decoded = decode_vrf_verification_key(&encoded).unwrap();
assert_eq!(decoded, max_key);
}
}