Skip to content

Commit

Permalink
feature: add conditional support for serde
Browse files Browse the repository at this point in the history
  • Loading branch information
hackaugusto committed Aug 11, 2023
1 parent 03f89f0 commit 8cf5e9f
Show file tree
Hide file tree
Showing 18 changed files with 204 additions and 14 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ harness = false
[features]
default = ["blake3/default", "std", "winter_crypto/default", "winter_math/default", "winter_utils/default"]
std = ["blake3/std", "winter_crypto/std", "winter_math/std", "winter_utils/std"]
serde = ["winter_math/serde", "dep:serde", "serde/alloc"]

[dependencies]
blake3 = { version = "1.4", default-features = false }
winter_crypto = { version = "0.6", package = "winter-crypto", default-features = false }
winter_math = { version = "0.6", package = "winter-math", default-features = false }
winter_utils = { version = "0.6", package = "winter-utils", default-features = false }
serde = { version = "1.0", features = [ "derive" ], optional = true, default-features = false }

[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }
Expand Down
21 changes: 20 additions & 1 deletion src/hash/blake/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use super::{Digest, ElementHasher, Felt, FieldElement, Hasher, StarkField};
use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
use crate::utils::{
bytes_to_hex_string, hex_to_bytes, string::String, ByteReader, ByteWriter, Deserializable,
DeserializationError, HexParseError, Serializable,
};
use core::{
mem::{size_of, transmute, transmute_copy},
ops::Deref,
Expand All @@ -24,6 +27,8 @@ const DIGEST20_BYTES: usize = 20;
/// Note: `N` can't be greater than `32` because [`Digest::as_bytes`] currently supports only 32
/// bytes.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(into = "String", try_from = "&str"))]
pub struct Blake3Digest<const N: usize>([u8; N]);

impl<const N: usize> Default for Blake3Digest<N> {
Expand Down Expand Up @@ -52,6 +57,20 @@ impl<const N: usize> From<[u8; N]> for Blake3Digest<N> {
}
}

impl<const N: usize> From<Blake3Digest<N>> for String {
fn from(value: Blake3Digest<N>) -> Self {
bytes_to_hex_string(value.as_bytes())
}
}

impl<const N: usize> TryFrom<&str> for Blake3Digest<N> {
type Error = HexParseError;

fn try_from(value: &str) -> Result<Self, Self::Error> {
hex_to_bytes(value).map(|v| v.into())
}
}

impl<const N: usize> Serializable for Blake3Digest<N> {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_bytes(&self.0);
Expand Down
99 changes: 87 additions & 12 deletions src/hash/rpo/digest.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
use super::{Digest, Felt, StarkField, DIGEST_SIZE, ZERO};
use crate::utils::{
string::String, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
bytes_to_hex_string, hex_to_bytes, string::String, ByteReader, ByteWriter, Deserializable,
DeserializationError, HexParseError, Serializable,
};
use core::{cmp::Ordering, fmt::Display, ops::Deref};

/// The number of bytes needed to encoded a digest
pub const DIGEST_BYTES: usize = 32;

// DIGEST TRAIT IMPLEMENTATIONS
// ================================================================================================

#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(into = "String", try_from = "&str"))]
pub struct RpoDigest([Felt; DIGEST_SIZE]);

impl RpoDigest {
Expand All @@ -19,7 +25,7 @@ impl RpoDigest {
self.as_ref()
}

pub fn as_bytes(&self) -> [u8; 32] {
pub fn as_bytes(&self) -> [u8; DIGEST_BYTES] {
<Self as Digest>::as_bytes(self)
}

Expand All @@ -32,8 +38,8 @@ impl RpoDigest {
}

impl Digest for RpoDigest {
fn as_bytes(&self) -> [u8; 32] {
let mut result = [0; 32];
fn as_bytes(&self) -> [u8; DIGEST_BYTES] {
let mut result = [0; DIGEST_BYTES];

result[..8].copy_from_slice(&self.0[0].as_int().to_le_bytes());
result[8..16].copy_from_slice(&self.0[1].as_int().to_le_bytes());
Expand Down Expand Up @@ -107,18 +113,73 @@ impl From<RpoDigest> for [u64; DIGEST_SIZE] {
}
}

impl From<&RpoDigest> for [u8; 32] {
impl From<&RpoDigest> for [u8; DIGEST_BYTES] {
fn from(value: &RpoDigest) -> Self {
value.as_bytes()
}
}

impl From<RpoDigest> for [u8; 32] {
impl From<RpoDigest> for [u8; DIGEST_BYTES] {
fn from(value: RpoDigest) -> Self {
value.as_bytes()
}
}

impl From<RpoDigest> for String {
fn from(value: RpoDigest) -> Self {
bytes_to_hex_string(value.as_bytes())
}
}

impl From<&RpoDigest> for String {
fn from(value: &RpoDigest) -> Self {
(*value).into()
}
}

impl TryFrom<[u8; DIGEST_BYTES]> for RpoDigest {
type Error = HexParseError;

fn try_from(value: [u8; DIGEST_BYTES]) -> Result<Self, Self::Error> {
// Note: the input length is known, the conversion from slice to array must succeed so the
// `unwrap`s below are safe
let a = u64::from_le_bytes(value[0..8].try_into().unwrap());
let b = u64::from_le_bytes(value[8..16].try_into().unwrap());
let c = u64::from_le_bytes(value[16..24].try_into().unwrap());
let d = u64::from_le_bytes(value[24..32].try_into().unwrap());

if [a, b, c, d].iter().any(|v| *v >= Felt::MODULUS) {
return Err(HexParseError::OutOfRange);
}

Ok(RpoDigest([Felt::new(a), Felt::new(b), Felt::new(c), Felt::new(d)]))
}
}

impl TryFrom<&str> for RpoDigest {
type Error = HexParseError;

fn try_from(value: &str) -> Result<Self, Self::Error> {
hex_to_bytes(value).and_then(|v| v.try_into())
}
}

impl TryFrom<String> for RpoDigest {
type Error = HexParseError;

fn try_from(value: String) -> Result<Self, Self::Error> {
value.as_str().try_into()
}
}

impl TryFrom<&String> for RpoDigest {
type Error = HexParseError;

fn try_from(value: &String) -> Result<Self, Self::Error> {
value.as_str().try_into()
}
}

impl Deref for RpoDigest {
type Target = [Felt; DIGEST_SIZE];

Expand Down Expand Up @@ -158,9 +219,8 @@ impl PartialOrd for RpoDigest {

impl Display for RpoDigest {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
for byte in self.as_bytes() {
write!(f, "{byte:02x}")?;
}
let encoded: String = self.into();
write!(f, "{}", encoded)?;
Ok(())
}
}
Expand All @@ -170,8 +230,7 @@ impl Display for RpoDigest {

#[cfg(test)]
mod tests {

use super::{Deserializable, Felt, RpoDigest, Serializable};
use super::{Deserializable, Felt, RpoDigest, Serializable, DIGEST_BYTES};
use crate::utils::SliceReader;
use rand_utils::rand_value;

Expand All @@ -186,11 +245,27 @@ mod tests {

let mut bytes = vec![];
d1.write_into(&mut bytes);
assert_eq!(32, bytes.len());
assert_eq!(DIGEST_BYTES, bytes.len());

let mut reader = SliceReader::new(&bytes);
let d2 = RpoDigest::read_from(&mut reader).unwrap();

assert_eq!(d1, d2);
}

#[cfg(feature = "std")]
#[test]
fn digest_encoding() {
let digest = RpoDigest([
Felt::new(rand_value()),
Felt::new(rand_value()),
Felt::new(rand_value()),
Felt::new(rand_value()),
]);

let string: String = digest.into();
let round_trip: RpoDigest = string.try_into().expect("decoding failed");

assert_eq!(digest, round_trip);
}
}
3 changes: 3 additions & 0 deletions src/merkle/delta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use super::{empty_roots::EMPTY_WORD, Felt, SimpleSmt};
/// [RpoDigest] represents the root of the Merkle tree and [MerkleTreeDelta] represents the
/// differences between the initial and final Merkle tree states.
#[derive(Default, Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct MerkleStoreDelta(pub Vec<(RpoDigest, MerkleTreeDelta)>);

// MERKLE TREE DELTA
Expand All @@ -26,6 +27,7 @@ pub struct MerkleStoreDelta(pub Vec<(RpoDigest, MerkleTreeDelta)>);
/// - updated_slots: index-value pairs of slots where values were set to non [ZERO; 4] values.
#[cfg(not(test))]
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct MerkleTreeDelta {
depth: u8,
cleared_slots: Vec<u64>,
Expand Down Expand Up @@ -107,6 +109,7 @@ pub fn merkle_tree_delta<T: KvMap<RpoDigest, StoreNode>>(
// --------------------------------------------------------------------------------------------
#[cfg(test)]
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct MerkleTreeDelta {
pub depth: u8,
pub cleared_slots: Vec<u64>,
Expand Down
1 change: 1 addition & 0 deletions src/merkle/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use core::fmt::Display;
/// The root is represented by the pair $(0, 0)$, its left child is $(1, 0)$ and its right child
/// $(1, 1)$.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct NodeIndex {
depth: u8,
value: u64,
Expand Down
1 change: 1 addition & 0 deletions src/merkle/merkle_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use winter_math::log2;

/// A fully-balanced binary Merkle tree (i.e., a tree where the number of leaves is a power of two).
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct MerkleTree {
nodes: Vec<RpoDigest>,
}
Expand Down
1 change: 1 addition & 0 deletions src/merkle/mmr/accumulator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use super::{
};

#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct MmrPeaks {
/// The number of leaves is used to differentiate accumulators that have the same number of
/// peaks. This happens because the number of peaks goes up-and-down as the structure is used
Expand Down
1 change: 1 addition & 0 deletions src/merkle/mmr/full.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use std::error::Error;
/// Since this is a full representation of the MMR, elements are never removed and the MMR will
/// grow roughly `O(2n)` in number of leaf elements.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Mmr {
/// Refer to the `forest` method documentation for details of the semantics of this value.
pub(super) forest: usize,
Expand Down
1 change: 1 addition & 0 deletions src/merkle/mmr/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use super::super::MerklePath;
use super::full::{high_bitmask, leaf_to_corresponding_tree};

#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct MmrProof {
/// The state of the MMR when the MmrProof was created.
pub forest: usize,
Expand Down
1 change: 1 addition & 0 deletions src/merkle/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::hash::rpo::RpoDigest;

/// Representation of a node with two children used for iterating over containers.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct InnerNodeInfo {
pub value: RpoDigest,
pub left: RpoDigest,
Expand Down
1 change: 1 addition & 0 deletions src/merkle/partial_mt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const EMPTY_DIGEST: RpoDigest = RpoDigest::new([ZERO; 4]);
///
/// The root of the tree is recomputed on each new leaf update.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct PartialMerkleTree {
max_depth: u8,
nodes: BTreeMap<NodeIndex, RpoDigest>,
Expand Down
1 change: 1 addition & 0 deletions src/merkle/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use core::ops::{Deref, DerefMut};

/// A merkle path container, composed of a sequence of nodes of a Merkle tree.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct MerklePath {
nodes: Vec<RpoDigest>,
}
Expand Down
2 changes: 2 additions & 0 deletions src/merkle/simple_smt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod tests;
///
/// The root of the tree is recomputed on each new leaf update.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct SimpleSmt {
depth: u8,
root: RpoDigest,
Expand Down Expand Up @@ -265,6 +266,7 @@ impl SimpleSmt {
// ================================================================================================

#[derive(Debug, Default, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
struct BranchNode {
left: RpoDigest,
right: RpoDigest,
Expand Down
2 changes: 2 additions & 0 deletions src/merkle/store/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub type DefaultMerkleStore = MerkleStore<BTreeMap<RpoDigest, StoreNode>>;
pub type RecordingMerkleStore = MerkleStore<RecordingMap<RpoDigest, StoreNode>>;

#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct StoreNode {
left: RpoDigest,
right: RpoDigest,
Expand Down Expand Up @@ -87,6 +88,7 @@ pub struct StoreNode {
/// assert_eq!(store.num_internal_nodes() - 255, 10);
/// ```
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct MerkleStore<T: KvMap<RpoDigest, StoreNode> = BTreeMap<RpoDigest, StoreNode>> {
nodes: T,
}
Expand Down
1 change: 1 addition & 0 deletions src/merkle/tiered_smt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ mod tests;
/// - Leaf node at depths 16, 32, or 64: hash(key, value, domain=depth).
/// - Leaf node at depth 64: hash([key_0, value_0, ..., key_n, value_n], domain=64).
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct TieredSmt {
root: RpoDigest,
nodes: NodeStore,
Expand Down
1 change: 1 addition & 0 deletions src/merkle/tiered_smt/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const MAX_DEPTH: u8 = super::TieredSmt::MAX_DEPTH;
/// represent leaf nodes in a Tiered Sparse Merkle tree. In the current implementation, [BTreeSet]s
/// are used to determine the position of the leaves in the tree.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct NodeStore {
nodes: BTreeMap<NodeIndex, RpoDigest>,
upper_leaves: BTreeSet<NodeIndex>,
Expand Down
2 changes: 2 additions & 0 deletions src/merkle/tiered_smt/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const MAX_DEPTH: u8 = super::TieredSmt::MAX_DEPTH;
/// The store supports lookup by the full key (i.e. [RpoDigest]) as well as by the 64-bit key
/// prefix.
#[derive(Debug, Default, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct ValueStore {
values: BTreeMap<u64, StoreEntry>,
}
Expand Down Expand Up @@ -173,6 +174,7 @@ impl ValueStore {
/// An entry can contain either a single key-value pair or a vector of key-value pairs sorted by
/// key.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum StoreEntry {
Single((RpoDigest, Word)),
List(Vec<(RpoDigest, Word)>),
Expand Down
Loading

0 comments on commit 8cf5e9f

Please sign in to comment.