use core::str::FromStr;
use serde::{Deserialize, Deserializer};
use subtle_encoding::base64;
use tracing::{error, trace};
use super::{errors::Error, key_utils::decode_bech32};
#[derive(Debug)]
pub enum EncodedPubKey {
Bech32(Vec<u8>),
Proto(ProtoAny),
}
impl EncodedPubKey {
pub fn into_bytes(self) -> Vec<u8> {
match self {
Self::Bech32(vec) => vec,
Self::Proto(proto) => proto.key,
}
}
}
#[derive(Debug, Deserialize)]
pub struct ProtoAny {
#[serde(alias = "@type")]
tpe: String,
#[serde(deserialize_with = "deserialize_key")]
key: Vec<u8>,
}
fn deserialize_key<'de, D>(deser: D) -> Result<Vec<u8>, D::Error>
where
D: Deserializer<'de>,
{
let based64_encoded: Result<String, _> = Deserialize::deserialize(deser);
let value = base64::decode(based64_encoded?)
.map_err(|e| serde::de::Error::custom(format!("error in decoding: {e}")))?;
Ok(value)
}
impl FromStr for EncodedPubKey {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let maybe_json: Result<ProtoAny, _> = serde_json::from_str(s);
match maybe_json {
Ok(proto) => {
trace!(
"deserialized the encoded pub key into a ProtoAny of type '{}'",
proto.tpe
);
if proto.tpe != "/cosmos.crypto.secp256k1.PubKey"
&& !proto.tpe.ends_with(".ethsecp256k1.PubKey")
{
Err(Error::only_secp256k1_public_key_supported(proto.tpe))
} else {
Ok(Self::Proto(proto))
}
}
Err(e) if e.classify() == serde_json::error::Category::Syntax => {
trace!("using Bech32 to interpret the encoded pub key '{}'", s);
Ok(Self::Bech32(decode_bech32(s)?))
}
Err(e) => {
error!(
"the encoded pub key is not in a valid format: '{}', error: {}",
s, e
);
Err(Error::encoded_public_key(s.to_string(), e))
}
}
}
}