diff --git a/src/field.rs b/src/field.rs index f174099..e488879 100644 --- a/src/field.rs +++ b/src/field.rs @@ -1,19 +1,11 @@ -use crate::util::{keccak256, trim_hex_prefix}; +use crate::util::{bytes_from_hex, deserialize_bytes, keccak256, serialize_bytes}; use ark_bn254::Fr as ArkField; use ark_ff::{BigInteger as _, PrimeField as _}; -use core::{ - fmt::{Formatter, Result as FmtResult}, - str, - str::FromStr, -}; +use core::{str, str::FromStr}; use ff::{PrimeField as _, PrimeFieldRepr as _}; -use hex::encode_to_slice; use num_bigint::{BigInt, Sign}; use poseidon_rs::Fr as PosField; -use serde::{ - de::{Error as DeError, Visitor}, - Deserialize, Serialize, Serializer, -}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; /// An element of the BN254 scalar field Fr. /// @@ -77,36 +69,28 @@ impl From for BigInt { } } +/// Serialize a field element. +/// +/// For human readable formats a `0x` prefixed lower case hex string is used. +/// For binary formats a byte array is used. impl Serialize for Field { fn serialize(&self, serializer: S) -> Result { - if serializer.is_human_readable() { - // Write as a 0x prefixed lower-case hex string - let mut buffer = [0u8; 66]; - buffer[0] = b'0'; - buffer[1] = b'x'; - encode_to_slice(&self.0, &mut buffer[2..]).expect("the buffer is correctly sized"); - let string = str::from_utf8(&buffer).expect("the buffer is valid UTF-8"); - serializer.serialize_str(string) - } else { - // Write as bytes directly - serializer.serialize_bytes(&self.0) - } + serialize_bytes::<32, 66, S>(serializer, &self.0) } } /// Parse Hash from hex string. +/// /// Hex strings can be upper/lower/mixed case and have an optional `0x` prefix /// but they must always be exactly 32 bytes. +/// +/// Too large values are reduced modulo the field prime. impl FromStr for Field { type Err = hex::FromHexError; fn from_str(s: &str) -> Result { - let str = trim_hex_prefix(s); - let mut out = [0_u8; 32]; - hex::decode_to_slice(str, &mut out)?; - - // TODO: Reduce - Ok(Self(out)) + let bytes = bytes_from_hex::<32>(s)?; + Ok(Self::from_be_bytes_mod_order(&bytes[..])) } } @@ -114,47 +98,9 @@ impl FromStr for Field { /// Hex strings can be upper/lower/mixed case and have an optional `0x` prefix /// but they must always be exactly 32 bytes. impl<'de> Deserialize<'de> for Field { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - if deserializer.is_human_readable() { - deserializer.deserialize_str(StrVisitor) - } else { - // TODO: Reduce - <[u8; 32]>::deserialize(deserializer).map(Field) - } - } -} - -struct StrVisitor; - -impl<'de> Visitor<'de> for StrVisitor { - type Value = Field; - - fn expecting(&self, formatter: &mut Formatter) -> FmtResult { - formatter.write_str("a 32 byte hex string") - } - - fn visit_borrowed_str(self, value: &'de str) -> Result - where - E: DeError, - { - Field::from_str(value).map_err(|e| E::custom(format!("Error in hex: {}", e))) - } - - fn visit_str(self, value: &str) -> Result - where - E: DeError, - { - Field::from_str(value).map_err(|e| E::custom(format!("Error in hex: {}", e))) - } - - fn visit_string(self, value: String) -> Result - where - E: DeError, - { - Field::from_str(&value).map_err(|e| E::custom(format!("Error in hex: {}", e))) + fn deserialize>(deserializer: D) -> Result { + let bytes = deserialize_bytes::<32, _>(deserializer)?; + Ok(Self::from_be_bytes_mod_order(&bytes)) } } diff --git a/src/hash.rs b/src/hash.rs index c3671af..790d0c1 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -1,13 +1,10 @@ +use crate::util::{bytes_from_hex, deserialize_bytes, serialize_bytes}; use ethers_core::types::U256; use num_bigint::{BigInt, Sign}; -use serde::{ - de::{Error as DeError, Visitor}, - ser::Error as _, - Deserialize, Serialize, -}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::{ fmt::{Debug, Display, Formatter, Result as FmtResult}, - str::{from_utf8, FromStr}, + str::FromStr, }; /// Container for 256-bit hash values. @@ -85,30 +82,15 @@ impl FromStr for Hash { type Err = hex::FromHexError; fn from_str(s: &str) -> Result { - let str = trim_hex_prefix(s); - let mut out = [0_u8; 32]; - hex::decode_to_slice(str, &mut out)?; - Ok(Self(out)) + bytes_from_hex::<32>(s).map(Self) } } /// Serialize hashes into human readable hex strings or byte arrays. /// Hex strings are lower case without prefix and always 32 bytes. impl Serialize for Hash { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - if serializer.is_human_readable() { - let mut hex_ascii = [0_u8; 64]; - hex::encode_to_slice(self.0, &mut hex_ascii) - .map_err(|e| S::Error::custom(format!("Error hex encoding: {}", e)))?; - from_utf8(&hex_ascii) - .map_err(|e| S::Error::custom(format!("Invalid hex encoding: {}", e)))? - .serialize(serializer) - } else { - self.0.serialize(serializer) - } + fn serialize(&self, serializer: S) -> Result { + serialize_bytes::<32, 66, S>(serializer, &self.0) } } @@ -116,55 +98,9 @@ impl Serialize for Hash { /// Hex strings can be upper/lower/mixed case and have an optional `0x` prefix /// but they must always be exactly 32 bytes. impl<'de> Deserialize<'de> for Hash { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - if deserializer.is_human_readable() { - deserializer.deserialize_str(HashStrVisitor) - } else { - <[u8; 32]>::deserialize(deserializer).map(Hash) - } - } -} - -struct HashStrVisitor; - -impl<'de> Visitor<'de> for HashStrVisitor { - type Value = Hash; - - fn expecting(&self, formatter: &mut Formatter) -> FmtResult { - formatter.write_str("a 32 byte hex string") - } - - fn visit_borrowed_str(self, value: &'de str) -> Result - where - E: DeError, - { - Hash::from_str(value).map_err(|e| E::custom(format!("Error in hex: {}", e))) - } - - fn visit_str(self, value: &str) -> Result - where - E: DeError, - { - Hash::from_str(value).map_err(|e| E::custom(format!("Error in hex: {}", e))) - } - - fn visit_string(self, value: String) -> Result - where - E: DeError, - { - Hash::from_str(&value).map_err(|e| E::custom(format!("Error in hex: {}", e))) - } -} - -/// Helper function to optionally remove `0x` prefix from hex strings. -fn trim_hex_prefix(str: &str) -> &str { - if str.len() >= 2 && (&str[..2] == "0x" || &str[..2] == "0X") { - &str[2..] - } else { - str + fn deserialize>(deserializer: D) -> Result { + let bytes = deserialize_bytes::<32, _>(deserializer)?; + Ok(Self(bytes)) } } @@ -179,14 +115,14 @@ pub mod test { let hash = Hash([0; 32]); assert_eq!( to_string(&hash).unwrap(), - "\"0000000000000000000000000000000000000000000000000000000000000000\"" + "\"0x0000000000000000000000000000000000000000000000000000000000000000\"" ); let hash = Hash(hex!( "1c4823575d154474ee3e5ac838d002456a815181437afd14f126da58a9912bbe" )); assert_eq!( to_string(&hash).unwrap(), - "\"1c4823575d154474ee3e5ac838d002456a815181437afd14f126da58a9912bbe\"" + "\"0x1c4823575d154474ee3e5ac838d002456a815181437afd14f126da58a9912bbe\"" ); } diff --git a/src/util.rs b/src/util.rs index 7ce8e18..698f638 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,3 +1,11 @@ +use core::{ + fmt::{Formatter, Result as FmtResult}, + str, +}; +use serde::{ + de::{Error as DeError, Visitor}, + Deserializer, Serializer, +}; use tiny_keccak::{Hasher as _, Keccak}; pub(crate) fn keccak256(bytes: &[u8]) -> [u8; 32] { @@ -8,11 +16,87 @@ pub(crate) fn keccak256(bytes: &[u8]) -> [u8; 32] { output } -/// Helper function to optionally remove `0x` prefix from hex strings. -pub(crate) fn trim_hex_prefix(str: &str) -> &str { +/// Helper to serialize byte arrays +pub(crate) fn serialize_bytes( + serializer: S, + bytes: &[u8; N], +) -> Result { + // TODO: Replace `M` with a const expression once it's stable. + debug_assert_eq!(M, 2 * N + 2); + if serializer.is_human_readable() { + // Write as a 0x prefixed lower-case hex string + let mut buffer = [0u8; M]; + buffer[0] = b'0'; + buffer[1] = b'x'; + hex::encode_to_slice(&bytes[..], &mut buffer[2..]).expect("the buffer is correctly sized"); + let string = str::from_utf8(&buffer).expect("the buffer is valid UTF-8"); + serializer.serialize_str(string) + } else { + // Write as bytes directly + serializer.serialize_bytes(&bytes[..]) + } +} + +/// Helper to deserialize byte arrays from hex strings +/// +/// TODO: How does it handle strings that are to short? +pub(crate) fn bytes_from_hex(s: &str) -> Result<[u8; N], hex::FromHexError> { + let str = trim_hex_prefix(s); + let mut result = [0_u8; N]; + hex::decode_to_slice(str, &mut result)?; + Ok(result) +} + +/// Helper function to remove optionally `0x` prefix from hex strings. +fn trim_hex_prefix(str: &str) -> &str { if str.len() >= 2 && (&str[..2] == "0x" || &str[..2] == "0X") { &str[2..] } else { str } } + +pub(crate) fn deserialize_bytes<'de, const N: usize, D: Deserializer<'de>>( + deserializer: D, +) -> Result<[u8; N], D::Error> { + if deserializer.is_human_readable() { + struct StrVisitor; + impl<'de, const N: usize> Visitor<'de> for StrVisitor { + type Value = [u8; N]; + + fn expecting(&self, formatter: &mut Formatter) -> FmtResult { + write!(formatter, "a {N} byte hex string") + } + + fn visit_str(self, value: &str) -> Result + where + E: DeError, + { + bytes_from_hex(value).map_err(|e| E::custom(format!("Error in hex: {}", e))) + } + } + deserializer.deserialize_str(StrVisitor) + } else { + struct ByteVisitor; + impl<'de, const N: usize> Visitor<'de> for ByteVisitor { + type Value = [u8; N]; + + fn expecting(&self, formatter: &mut Formatter) -> FmtResult { + write!(formatter, "{N} bytes of binary data") + } + + fn visit_bytes(self, value: &[u8]) -> Result + where + E: DeError, + { + if value.len() != N { + return Err(E::invalid_length(value.len(), &self)); + } + let mut result = [0_u8; N]; + result.copy_from_slice(value); + Ok(result) + } + } + deserializer.deserialize_bytes(ByteVisitor) + } +}