use serde::{de, Deserializer, Serializer};
use crate::BITCOIN;
pub mod base58 {
use super::*;
pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
where
T: AsRef<[u8]>,
S: Serializer,
{
let encoded = BITCOIN.encode(value.as_ref()).map_err(serde::ser::Error::custom)?;
serializer.serialize_str(&encoded)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
D: Deserializer<'de>,
{
struct Base58Visitor;
impl<'de> de::Visitor<'de> for Base58Visitor {
type Value = Vec<u8>;
fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter.write_str(
"a valid Base58 string"
)
}
fn visit_str<E>(self, value: &str) -> Result<Vec<u8>, E>
where
E: de::Error,
{
BITCOIN.decode(value).map_err(de::Error::custom)
}
fn visit_string<E>(self, value: String) -> Result<Vec<u8>, E>
where
E: de::Error,
{
self.visit_str(&value)
}
fn visit_bytes<E>(self, value: &[u8]) -> Result<Vec<u8>, E>
where
E: de::Error,
{
BITCOIN.decode(value).map_err(de::Error::custom)
}
}
deserializer.deserialize_str(Base58Visitor)
}
}
pub mod base58_24 {
use super::*;
pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
where
T: AsRef<[u8]>,
S: Serializer,
{
let bytes = value.as_ref();
if bytes.len() != 24 {
return Err(serde::ser::Error::custom(format!("expected 24 bytes, got {}", bytes.len())));
}
let encoded = BITCOIN.encode(bytes).map_err(serde::ser::Error::custom)?;
serializer.serialize_str(&encoded)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; 24], D::Error>
where
D: Deserializer<'de>,
{
struct Base58Visitor;
impl<'de> de::Visitor<'de> for Base58Visitor {
type Value = [u8; 24];
fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter.write_str("a valid Base58 string representing exactly 24 bytes")
}
fn visit_str<E>(self, value: &str) -> Result<[u8; 24], E>
where
E: de::Error,
{
let vec = BITCOIN.decode(value).map_err(de::Error::custom)?;
if vec.len() != 24 {
return Err(de::Error::custom(format!("expected 24 bytes, got {}", vec.len())));
}
Ok(vec.try_into().expect("length already checked"))
}
fn visit_string<E>(self, value: String) -> Result<[u8; 24], E>
where
E: de::Error,
{
self.visit_str(&value)
}
fn visit_bytes<E>(self, value: &[u8]) -> Result<[u8; 24], E>
where
E: de::Error,
{
let vec = BITCOIN.decode(value).map_err(de::Error::custom)?;
if vec.len() != 24 {
return Err(de::Error::custom(format!("expected 24 bytes, got {}", vec.len())));
}
Ok(vec.try_into().expect("length already checked"))
}
}
deserializer.deserialize_str(Base58Visitor)
}
}
pub mod base58_32 {
use super::*;
pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
where
T: AsRef<[u8]>,
S: Serializer,
{
let bytes = value.as_ref();
if bytes.len() != 32 {
return Err(serde::ser::Error::custom(format!("expected 32 bytes, got {}", bytes.len())));
}
let encoded = BITCOIN.encode(bytes).map_err(serde::ser::Error::custom)?;
serializer.serialize_str(&encoded)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error>
where
D: Deserializer<'de>,
{
struct Base58Visitor;
impl<'de> de::Visitor<'de> for Base58Visitor {
type Value = [u8; 32];
fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter.write_str("a valid Base58 string representing exactly 32 bytes")
}
fn visit_str<E>(self, value: &str) -> Result<[u8; 32], E>
where
E: de::Error,
{
let vec = BITCOIN.decode(value).map_err(de::Error::custom)?;
if vec.len() != 32 {
return Err(de::Error::custom(format!("expected 32 bytes, got {}", vec.len())));
}
Ok(vec.try_into().expect("length already checked"))
}
fn visit_string<E>(self, value: String) -> Result<[u8; 32], E>
where
E: de::Error,
{
self.visit_str(&value)
}
fn visit_bytes<E>(self, value: &[u8]) -> Result<[u8; 32], E>
where
E: de::Error,
{
let vec = BITCOIN.decode(value).map_err(de::Error::custom)?;
if vec.len() != 32 {
return Err(de::Error::custom(format!("expected 32 bytes, got {}", vec.len())));
}
Ok(vec.try_into().expect("length already checked"))
}
}
deserializer.deserialize_str(Base58Visitor)
}
}
pub mod base58_48 {
use super::*;
pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
where
T: AsRef<[u8]>,
S: Serializer,
{
let bytes = value.as_ref();
if bytes.len() != 48 {
return Err(serde::ser::Error::custom(format!("expected 48 bytes, got {}", bytes.len())));
}
let encoded = BITCOIN.encode(bytes).map_err(serde::ser::Error::custom)?;
serializer.serialize_str(&encoded)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; 48], D::Error>
where
D: Deserializer<'de>,
{
struct Base58Visitor;
impl<'de> de::Visitor<'de> for Base58Visitor {
type Value = [u8; 48];
fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter.write_str("a valid Base58 string representing exactly 48 bytes")
}
fn visit_str<E>(self, value: &str) -> Result<[u8; 48], E>
where
E: de::Error,
{
let vec = BITCOIN.decode(value).map_err(de::Error::custom)?;
if vec.len() != 48 {
return Err(de::Error::custom(format!("expected 48 bytes, got {}", vec.len())));
}
Ok(vec.try_into().expect("length already checked"))
}
fn visit_string<E>(self, value: String) -> Result<[u8; 48], E>
where
E: de::Error,
{
self.visit_str(&value)
}
fn visit_bytes<E>(self, value: &[u8]) -> Result<[u8; 48], E>
where
E: de::Error,
{
let vec = BITCOIN.decode(value).map_err(de::Error::custom)?;
if vec.len() != 48 {
return Err(de::Error::custom(format!("expected 48 bytes, got {}", vec.len())));
}
Ok(vec.try_into().expect("length already checked"))
}
}
deserializer.deserialize_str(Base58Visitor)
}
}
pub mod base58_64 {
use super::*;
pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
where
T: AsRef<[u8]>,
S: Serializer,
{
let bytes = value.as_ref();
if bytes.len() != 64 {
return Err(serde::ser::Error::custom(format!("expected 64 bytes, got {}", bytes.len())));
}
let encoded = BITCOIN.encode(bytes).map_err(serde::ser::Error::custom)?;
serializer.serialize_str(&encoded)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; 64], D::Error>
where
D: Deserializer<'de>,
{
struct Base58Visitor;
impl<'de> de::Visitor<'de> for Base58Visitor {
type Value = [u8; 64];
fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter.write_str("a valid Base58 string representing exactly 64 bytes")
}
fn visit_str<E>(self, value: &str) -> Result<[u8; 64], E>
where
E: de::Error,
{
let vec = BITCOIN.decode(value).map_err(de::Error::custom)?;
if vec.len() != 64 {
return Err(de::Error::custom(format!("expected 64 bytes, got {}", vec.len())));
}
Ok(vec.try_into().expect("length already checked"))
}
fn visit_string<E>(self, value: String) -> Result<[u8; 64], E>
where
E: de::Error,
{
self.visit_str(&value)
}
fn visit_bytes<E>(self, value: &[u8]) -> Result<[u8; 64], E>
where
E: de::Error,
{
let vec = BITCOIN.decode(value).map_err(de::Error::custom)?;
if vec.len() != 64 {
return Err(de::Error::custom(format!("expected 64 bytes, got {}", vec.len())));
}
Ok(vec.try_into().expect("length already checked"))
}
}
deserializer.deserialize_str(Base58Visitor)
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde::{Deserialize, Serialize};
use serde::de::{IntoDeserializer, value::{StringDeserializer, BytesDeserializer, Error as ValueError}};
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct VarLenPayload {
#[serde(with = "base58")]
data: Vec<u8>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Fixed24Payload { #[serde(with = "base58_24")] data: [u8; 24], }
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Fixed32Payload { #[serde(with = "base58_32")] data: [u8; 32], }
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Fixed48Payload { #[serde(with = "base58_48")] data: [u8; 48], }
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Fixed64Payload { #[serde(with = "base58_64")] data: [u8; 64], }
#[derive(Debug, Serialize)] struct Dyn24<'a> { #[serde(with = "base58_24")] data: &'a [u8], }
#[derive(Debug, Serialize)] struct Dyn32<'a> { #[serde(with = "base58_32")] data: &'a [u8], }
#[derive(Debug, Serialize)] struct Dyn48<'a> { #[serde(with = "base58_48")] data: &'a [u8], }
#[derive(Debug, Serialize)] struct Dyn64<'a> { #[serde(with = "base58_64")] data: &'a [u8], }
#[test]
fn test_var_len_success() {
let payload = VarLenPayload { data: b"Hello World".to_vec() };
let serialized = serde_json::to_string(&payload).unwrap();
assert_eq!(serialized, r#"{"data":"JxF12TrwUP45BMd"}"#);
assert_eq!(payload, serde_json::from_str(&serialized).unwrap());
}
#[test]
fn test_var_len_serialization_size_limit() {
let payload = VarLenPayload { data: vec![0u8; 1025] };
assert!(serde_json::to_string(&payload).is_err());
}
#[test]
fn test_var_len_wrong_type_expecting() {
let res: Result<VarLenPayload, _> = serde_json::from_str(r#"{"data":123}"#);
assert!(res.unwrap_err().to_string().contains("a valid Base58 string"));
}
#[test]
fn test_exhaustive_visitors_var_len() {
let s = "JxF12TrwUP45BMd".to_string();
let de_str: StringDeserializer<ValueError> = s.into_deserializer();
assert!(base58::deserialize(de_str).is_ok());
let b = b"JxF12TrwUP45BMd".as_slice();
let de_bytes: BytesDeserializer<'_, ValueError> = b.into_deserializer();
assert!(base58::deserialize(de_bytes).is_ok());
let s_err = "0OIl".to_string();
let de_err: StringDeserializer<ValueError> = s_err.into_deserializer();
assert!(base58::deserialize(de_err).is_err());
}
macro_rules! test_fixed_visitors {
($module:ident, $size:expr) => {
let valid_str = BITCOIN.encode(&[$size as u8; $size]).unwrap();
let wrong_len_str = BITCOIN.encode(&[0u8; 1]).unwrap();
let de_s: StringDeserializer<ValueError> = valid_str.clone().into_deserializer();
assert!($module::deserialize(de_s).is_ok());
let de_b: BytesDeserializer<'_, ValueError> = valid_str.as_bytes().into_deserializer();
assert!($module::deserialize(de_b).is_ok());
let de_bad_s: StringDeserializer<ValueError> = wrong_len_str.clone().into_deserializer();
assert!($module::deserialize(de_bad_s).is_err());
let de_inv_s: StringDeserializer<ValueError> = "0OIl".to_string().into_deserializer();
assert!($module::deserialize(de_inv_s).is_err());
};
}
#[test]
fn test_exhaustive_visitors_fixed_lens() {
test_fixed_visitors!(base58_24, 24);
test_fixed_visitors!(base58_32, 32);
test_fixed_visitors!(base58_48, 48);
test_fixed_visitors!(base58_64, 64);
}
#[test]
fn test_serialize_length_mismatch() {
assert!(serde_json::to_string(&Dyn24 { data: &[0u8; 2] }).is_err());
assert!(serde_json::to_string(&Dyn32 { data: &[0u8; 2] }).is_err());
assert!(serde_json::to_string(&Dyn48 { data: &[0u8; 2] }).is_err());
assert!(serde_json::to_string(&Dyn64 { data: &[0u8; 2] }).is_err());
}
#[test]
fn test_fixed_lengths_wrong_type_expecting() {
assert!(serde_json::from_str::<Fixed24Payload>(r#"{"data":0}"#).is_err());
assert!(serde_json::from_str::<Fixed32Payload>(r#"{"data":0}"#).is_err());
assert!(serde_json::from_str::<Fixed48Payload>(r#"{"data":0}"#).is_err());
assert!(serde_json::from_str::<Fixed64Payload>(r#"{"data":0}"#).is_err());
}
}