Updated Makefile and Readme

This commit is contained in:
Paul-Henry Kajfasz
2024-08-16 15:07:27 -07:00
parent d92fae7f82
commit ad0f472708
50 changed files with 416 additions and 278 deletions

View File

@@ -1,7 +1,9 @@
use super::{math::FalconFelt, Nonce, Polynomial, Rpo256, Word, MODULUS, N, ZERO};
use alloc::vec::Vec;
use num::Zero;
use super::{math::FalconFelt, Nonce, Polynomial, Rpo256, Word, MODULUS, N, ZERO};
// HASH-TO-POINT FUNCTIONS
// ================================================================================================

View File

@@ -15,12 +15,13 @@ pub use secret_key::SecretKey;
#[cfg(test)]
mod tests {
use crate::{dsa::rpo_falcon512::SecretKey, Word, ONE};
use rand::SeedableRng;
use rand_chacha::ChaCha20Rng;
use winter_math::FieldElement;
use winter_utils::{Deserializable, Serializable};
use crate::{dsa::rpo_falcon512::SecretKey, Word, ONE};
#[test]
fn test_falcon_verification() {
let seed = [0_u8; 32];

View File

@@ -1,13 +1,14 @@
use crate::dsa::rpo_falcon512::FALCON_ENCODING_BITS;
use alloc::string::ToString;
use core::ops::Deref;
use num::Zero;
use super::{
super::{Rpo256, LOG_N, N, PK_LEN},
ByteReader, ByteWriter, Deserializable, DeserializationError, FalconFelt, Felt, Polynomial,
Serializable, Signature, Word,
};
use alloc::string::ToString;
use core::ops::Deref;
use num::Zero;
use crate::dsa::rpo_falcon512::FALCON_ENCODING_BITS;
// PUBLIC KEY
// ================================================================================================
@@ -116,7 +117,7 @@ impl Deserializable for PubKeyPoly {
if acc_len >= FALCON_ENCODING_BITS {
acc_len -= FALCON_ENCODING_BITS;
let w = (acc >> acc_len) & 0x3FFF;
let w = (acc >> acc_len) & 0x3fff;
let element = w.try_into().map_err(|err| {
DeserializationError::InvalidValue(format!(
"Failed to decode public key: {err}"

View File

@@ -1,3 +1,11 @@
use alloc::{string::ToString, vec::Vec};
use num::Complex;
#[cfg(not(feature = "std"))]
use num::Float;
use num_complex::Complex64;
use rand::Rng;
use super::{
super::{
math::{ffldl, ffsampling, gram, normalize_tree, FalconFelt, FastFft, LdlTree, Polynomial},
@@ -10,13 +18,6 @@ use super::{
use crate::dsa::rpo_falcon512::{
hash_to_point::hash_to_point_rpo256, math::ntru_gen, SIG_NONCE_LEN, SK_LEN,
};
use alloc::{string::ToString, vec::Vec};
use num::Complex;
use num_complex::Complex64;
use rand::Rng;
#[cfg(not(feature = "std"))]
use num::Float;
// CONSTANTS
// ================================================================================================

View File

@@ -1,11 +1,12 @@
use super::{fft::FastFft, polynomial::Polynomial, samplerz::sampler_z};
use alloc::boxed::Box;
#[cfg(not(feature = "std"))]
use num::Float;
use num::{One, Zero};
use num_complex::{Complex, Complex64};
use rand::Rng;
#[cfg(not(feature = "std"))]
use num::Float;
use super::{fft::FastFft, polynomial::Polynomial, samplerz::sampler_z};
const SIGMIN: f64 = 1.2778336969128337;
@@ -80,11 +81,11 @@ pub fn normalize_tree(tree: &mut LdlTree, sigma: f64) {
LdlTree::Branch(_ell, left, right) => {
normalize_tree(left, sigma);
normalize_tree(right, sigma);
}
},
LdlTree::Leaf(vector) => {
vector[0] = Complex::new(sigma / vector[0].re.sqrt(), 0.0);
vector[1] = Complex64::zero();
}
},
}
}
@@ -110,7 +111,7 @@ pub fn ffsampling<R: Rng>(
let z0 = Polynomial::<Complex64>::merge_fft(&bold_z0.0, &bold_z0.1);
(z0, z1)
}
},
LdlTree::Leaf(value) => {
let z0 = sampler_z(t.0.coefficients[0].re, value[0].re, SIGMIN, &mut rng);
let z1 = sampler_z(t.1.coefficients[0].re, value[0].re, SIGMIN, &mut rng);
@@ -118,6 +119,6 @@ pub fn ffsampling<R: Rng>(
Polynomial::new(vec![Complex64::new(z0 as f64, 0.0)]),
Polynomial::new(vec![Complex64::new(z1 as f64, 0.0)]),
)
}
},
}
}

View File

@@ -1,14 +1,15 @@
use super::{field::FalconFelt, polynomial::Polynomial, Inverse};
use alloc::vec::Vec;
use core::{
f64::consts::PI,
ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign},
};
use num::{One, Zero};
use num_complex::Complex64;
#[cfg(not(feature = "std"))]
use num::Float;
use num::{One, Zero};
use num_complex::Complex64;
use super::{field::FalconFelt, polynomial::Polynomial, Inverse};
/// Implements Cyclotomic FFT without bitreversing the outputs, and using precomputed powers of the
/// 2n-th primitive root of unity.
@@ -102,7 +103,8 @@ where
array
}
/// Reorders the given elements in the array by reversing the binary expansions of their indices.
/// Reorders the given elements in the array by reversing the binary expansions of their
/// indices.
fn bitreverse_array<T>(array: &mut [T]) {
let n = array.len();
for i in 0..n {
@@ -118,19 +120,14 @@ where
///
/// Arguments:
///
/// - a : &mut [Self]
/// (a reference to) a mutable array of field elements which is to
/// be transformed under the FFT. The transformation happens in-
/// place.
/// - a : &mut [Self] (a reference to) a mutable array of field elements which is to be
/// transformed under the FFT. The transformation happens in- place.
///
/// - psi_rev: &[Self]
/// (a reference to) an array of powers of psi, from 0 to n-1,
/// but ordered by bit-reversed index. Here psi is a primitive root
/// of order 2n. You can use
/// `Self::bitreversed_powers(psi, n)` for this purpose, but this
/// trait implementation is not const. For the performance benefit
/// you want a precompiled array, which you can get if you can get
/// by implementing the same method and marking it "const".
/// - psi_rev: &[Self] (a reference to) an array of powers of psi, from 0 to n-1, but ordered
/// by bit-reversed index. Here psi is a primitive root of order 2n. You can use
/// `Self::bitreversed_powers(psi, n)` for this purpose, but this trait implementation is not
/// const. For the performance benefit you want a precompiled array, which you can get if you
/// can get by implementing the same method and marking it "const".
fn fft(a: &mut [Self], psi_rev: &[Self]) {
let n = a.len();
let mut t = n;
@@ -158,20 +155,15 @@ where
///
/// Arguments:
///
/// - a : &mut [Self]
/// (a reference to) a mutable array of field elements which is to
/// be transformed under the IFFT. The transformation happens in-
/// place.
/// - a : &mut [Self] (a reference to) a mutable array of field elements which is to be
/// transformed under the IFFT. The transformation happens in- place.
///
/// - psi_inv_rev: &[Self]
/// (a reference to) an array of powers of psi^-1, from 0 to n-1,
/// but ordered by bit-reversed index. Here psi is a primitive root of
/// order 2n. You can use
/// `Self::bitreversed_powers(Self::inverse_or_zero(psi), n)` for
/// this purpose, but this trait implementation is not const. For
/// the performance benefit you want a precompiled array, which you
/// can get if you can get by implementing the same methods and marking
/// them "const".
/// - psi_inv_rev: &[Self] (a reference to) an array of powers of psi^-1, from 0 to n-1, but
/// ordered by bit-reversed index. Here psi is a primitive root of order 2n. You can use
/// `Self::bitreversed_powers(Self::inverse_or_zero(psi), n)` for this purpose, but this
/// trait implementation is not const. For the performance benefit you want a precompiled
/// array, which you can get if you can get by implementing the same methods and marking them
/// "const".
fn ifft(a: &mut [Self], psi_inv_rev: &[Self], ninv: Self) {
let n = a.len();
let mut t = 1;

View File

@@ -1,8 +1,10 @@
use super::{fft::CyclotomicFourier, Inverse, MODULUS};
use alloc::string::String;
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use num::{One, Zero};
use super::{fft::CyclotomicFourier, Inverse, MODULUS};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct FalconFelt(u32);

View File

@@ -2,17 +2,19 @@
//!
//! It uses and acknowledges the work in:
//!
//! 1. The [reference](https://falcon-sign.info/impl/README.txt.html) implementation by Thomas Pornin.
//! 1. The [reference](https://falcon-sign.info/impl/README.txt.html) implementation by Thomas
//! Pornin.
//! 2. The [Rust](https://github.com/aszepieniec/falcon-rust) implementation by Alan Szepieniec.
use super::MODULUS;
use alloc::{string::String, vec::Vec};
use core::ops::MulAssign;
#[cfg(not(feature = "std"))]
use num::Float;
use num::{BigInt, FromPrimitive, One, Zero};
use num_complex::Complex64;
use rand::Rng;
#[cfg(not(feature = "std"))]
use num::Float;
use super::MODULUS;
mod fft;
pub use fft::{CyclotomicFourier, FastFft};
@@ -152,7 +154,7 @@ fn ntru_solve(
{
None
}
}
},
}
}

View File

@@ -1,12 +1,18 @@
use super::{field::FalconFelt, Inverse};
use crate::dsa::rpo_falcon512::{MODULUS, N};
use crate::Felt;
use alloc::vec::Vec;
use core::default::Default;
use core::fmt::Debug;
use core::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign};
use core::{
default::Default,
fmt::Debug,
ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign},
};
use num::{One, Zero};
use super::{field::FalconFelt, Inverse};
use crate::{
dsa::rpo_falcon512::{MODULUS, N},
Felt,
};
#[derive(Debug, Clone, Default)]
pub struct Polynomial<F> {
pub coefficients: Vec<F>,
@@ -134,8 +140,8 @@ impl<
Self::new(coefficients)
}
/// Computes the galois adjoint of the polynomial in the cyclotomic ring F\[ X \] / < X^n + 1 > ,
/// which corresponds to f(x^2).
/// Computes the galois adjoint of the polynomial in the cyclotomic ring F\[ X \] / < X^n + 1 >
/// , which corresponds to f(x^2).
pub fn galois_adjoint(&self) -> Self {
Self::new(
self.coefficients

View File

@@ -1,8 +1,8 @@
use core::f64::consts::LN_2;
use rand::Rng;
#[cfg(not(feature = "std"))]
use num::Float;
use rand::Rng;
/// Samples an integer from {0, ..., 18} according to the distribution χ, which is close to
/// the half-Gaussian distribution on the natural numbers with mean 0 and standard deviation
@@ -40,18 +40,18 @@ fn approx_exp(x: f64, ccs: f64) -> u64 {
// https://eprint.iacr.org/2018/1234
// https://github.com/raykzhao/gaussian
const C: [u64; 13] = [
0x00000004741183A3u64,
0x00000036548CFC06u64,
0x0000024FDCBF140Au64,
0x0000171D939DE045u64,
0x0000D00CF58F6F84u64,
0x000680681CF796E3u64,
0x002D82D8305B0FEAu64,
0x011111110E066FD0u64,
0x0555555555070F00u64,
0x155555555581FF00u64,
0x400000000002B400u64,
0x7FFFFFFFFFFF4800u64,
0x00000004741183a3u64,
0x00000036548cfc06u64,
0x0000024fdcbf140au64,
0x0000171d939de045u64,
0x0000d00cf58f6f84u64,
0x000680681cf796e3u64,
0x002d82d8305b0feau64,
0x011111110e066fd0u64,
0x0555555555070f00u64,
0x155555555581ff00u64,
0x400000000002b400u64,
0x7fffffffffff4800u64,
0x8000000000000000u64,
];
@@ -116,9 +116,10 @@ pub(crate) fn sampler_z<R: Rng>(mu: f64, sigma: f64, sigma_min: f64, rng: &mut R
#[cfg(all(test, feature = "std"))]
mod test {
use alloc::vec::Vec;
use rand::RngCore;
use std::{thread::sleep, time::Duration};
use rand::RngCore;
use super::{approx_exp, ber_exp, sampler_z};
/// RNG used only for testing purposes, whereby the produced

View File

@@ -9,9 +9,11 @@ mod keys;
mod math;
mod signature;
pub use self::keys::{PubKeyPoly, PublicKey, SecretKey};
pub use self::math::Polynomial;
pub use self::signature::{Signature, SignatureHeader, SignaturePoly};
pub use self::{
keys::{PubKeyPoly, PublicKey, SecretKey},
math::Polynomial,
signature::{Signature, SignatureHeader, SignaturePoly},
};
// CONSTANTS
// ================================================================================================

View File

@@ -1,6 +1,8 @@
use alloc::{string::ToString, vec::Vec};
use core::ops::Deref;
use num::Zero;
use super::{
hash_to_point::hash_to_point_rpo256,
keys::PubKeyPoly,
@@ -8,7 +10,6 @@ use super::{
ByteReader, ByteWriter, Deserializable, DeserializationError, Felt, Nonce, Rpo256,
Serializable, Word, LOG_N, MODULUS, N, SIG_L2_BOUND, SIG_POLY_BYTE_LEN,
};
use num::Zero;
// FALCON SIGNATURE
// ================================================================================================
@@ -38,8 +39,8 @@ use num::Zero;
/// The signature is serialized as:
/// 1. A header byte specifying the algorithm used to encode the coefficients of the `s2` polynomial
/// together with the degree of the irreducible polynomial phi. For RPO Falcon512, the header
/// byte is set to `10111001` which differentiates it from the standardized instantiation of
/// the Falcon signature.
/// byte is set to `10111001` which differentiates it from the standardized instantiation of the
/// Falcon signature.
/// 2. 40 bytes for the nonce.
/// 4. 625 bytes encoding the `s2` polynomial above.
///
@@ -355,10 +356,11 @@ fn are_coefficients_valid(x: &[i16]) -> bool {
#[cfg(test)]
mod tests {
use super::{super::SecretKey, *};
use rand::SeedableRng;
use rand_chacha::ChaCha20Rng;
use super::{super::SecretKey, *};
#[test]
fn test_serialization_round_trip() {
let seed = [0_u8; 32];

View File

@@ -1,8 +1,9 @@
use alloc::vec::Vec;
use proptest::prelude::*;
use rand_utils::rand_vector;
use super::*;
use alloc::vec::Vec;
#[test]
fn blake3_hash_elements() {

View File

@@ -4,42 +4,29 @@ use core::arch::x86_64::*;
// https://github.com/0xPolygonZero/plonky2/blob/main/plonky2/src/hash/arch/x86_64/poseidon_goldilocks_avx2_bmi2.rs
// Preliminary notes:
// 1. AVX does not support addition with carry but 128-bit (2-word) addition can be easily
// emulated. The method recognizes that for a + b overflowed iff (a + b) < a:
// i. res_lo = a_lo + b_lo
// ii. carry_mask = res_lo < a_lo
// iii. res_hi = a_hi + b_hi - carry_mask
// Notice that carry_mask is subtracted, not added. This is because AVX comparison instructions
// return -1 (all bits 1) for true and 0 for false.
// 1. AVX does not support addition with carry but 128-bit (2-word) addition can be easily emulated.
// The method recognizes that for a + b overflowed iff (a + b) < a: i. res_lo = a_lo + b_lo ii.
// carry_mask = res_lo < a_lo iii. res_hi = a_hi + b_hi - carry_mask Notice that carry_mask is
// subtracted, not added. This is because AVX comparison instructions return -1 (all bits 1) for
// true and 0 for false.
//
// 2. AVX does not have unsigned 64-bit comparisons. Those can be emulated with signed comparisons
// by recognizing that a <u b iff a + (1 << 63) <s b + (1 << 63), where the addition wraps around
// and the comparisons are unsigned and signed respectively. The shift function adds/subtracts
// 1 << 63 to enable this trick.
// Example: addition with carry.
// i. a_lo_s = shift(a_lo)
// ii. res_lo_s = a_lo_s + b_lo
// iii. carry_mask = res_lo_s <s a_lo_s
// iv. res_lo = shift(res_lo_s)
// v. res_hi = a_hi + b_hi - carry_mask
// The suffix _s denotes a value that has been shifted by 1 << 63. The result of addition is
// shifted if exactly one of the operands is shifted, as is the case on line ii. Line iii.
// performs a signed comparison res_lo_s <s a_lo_s on shifted values to emulate unsigned
// comparison res_lo <u a_lo on unshifted values. Finally, line iv. reverses the shift so the
// result can be returned.
// When performing a chain of calculations, we can often save instructions by letting the shift
// propagate through and only undoing it when necessary. For example, to compute the addition of
// three two-word (128-bit) numbers we can do:
// i. a_lo_s = shift(a_lo)
// ii. tmp_lo_s = a_lo_s + b_lo
// iii. tmp_carry_mask = tmp_lo_s <s a_lo_s
// iv. tmp_hi = a_hi + b_hi - tmp_carry_mask
// v. res_lo_s = tmp_lo_s + c_lo
// vi. res_carry_mask = res_lo_s <s tmp_lo_s
// vii. res_lo = shift(res_lo_s)
// viii. res_hi = tmp_hi + c_hi - res_carry_mask
// Notice that the above 3-value addition still only requires two calls to shift, just like our
// 2-value addition.
// and the comparisons are unsigned and signed respectively. The shift function adds/subtracts 1
// << 63 to enable this trick. Example: addition with carry. i. a_lo_s = shift(a_lo) ii. res_lo_s
// = a_lo_s + b_lo iii. carry_mask = res_lo_s <s a_lo_s iv. res_lo = shift(res_lo_s) v. res_hi =
// a_hi + b_hi - carry_mask The suffix _s denotes a value that has been shifted by 1 << 63. The
// result of addition is shifted if exactly one of the operands is shifted, as is the case on
// line ii. Line iii. performs a signed comparison res_lo_s <s a_lo_s on shifted values to
// emulate unsigned comparison res_lo <u a_lo on unshifted values. Finally, line iv. reverses the
// shift so the result can be returned. When performing a chain of calculations, we can often
// save instructions by letting the shift propagate through and only undoing it when necessary.
// For example, to compute the addition of three two-word (128-bit) numbers we can do: i. a_lo_s
// = shift(a_lo) ii. tmp_lo_s = a_lo_s + b_lo iii. tmp_carry_mask = tmp_lo_s <s a_lo_s iv. tmp_hi
// = a_hi + b_hi - tmp_carry_mask v. res_lo_s = tmp_lo_s + c_lo vi. res_carry_mask = res_lo_s <s
// tmp_lo_s vii. res_lo = shift(res_lo_s) viii. res_hi = tmp_hi + c_hi - res_carry_mask Notice
// that the above 3-value addition still only requires two calls to shift, just like our 2-value
// addition.
#[inline(always)]
pub fn branch_hint() {
@@ -60,10 +47,10 @@ pub fn branch_hint() {
}
macro_rules! map3 {
($f:ident::<$l:literal>, $v:ident) => {
($f:ident:: < $l:literal > , $v:ident) => {
($f::<$l>($v.0), $f::<$l>($v.1), $f::<$l>($v.2))
};
($f:ident::<$l:literal>, $v1:ident, $v2:ident) => {
($f:ident:: < $l:literal > , $v1:ident, $v2:ident) => {
($f::<$l>($v1.0, $v2.0), $f::<$l>($v1.1, $v2.1), $f::<$l>($v1.2, $v2.2))
};
($f:ident, $v:ident) => {
@@ -72,11 +59,11 @@ macro_rules! map3 {
($f:ident, $v0:ident, $v1:ident) => {
($f($v0.0, $v1.0), $f($v0.1, $v1.1), $f($v0.2, $v1.2))
};
($f:ident, rep $v0:ident, $v1:ident) => {
($f:ident,rep $v0:ident, $v1:ident) => {
($f($v0, $v1.0), $f($v0, $v1.1), $f($v0, $v1.2))
};
($f:ident, $v0:ident, rep $v1:ident) => {
($f:ident, $v0:ident,rep $v1:ident) => {
($f($v0.0, $v1), $f($v0.1, $v1), $f($v0.2, $v1))
};
}

View File

@@ -7,9 +7,9 @@
/// of two vectors in "frequency domain". This follows from the simple fact that every circulant
/// matrix has the columns of the discrete Fourier transform matrix as orthogonal eigenvectors.
/// The implementation also avoids the use of 3-point FFTs, and 3-point iFFTs, and substitutes that
/// with explicit expressions. It also avoids, due to the form of our matrix in the frequency domain,
/// divisions by 2 and repeated modular reductions. This is because of our explicit choice of
/// an MDS matrix that has small powers of 2 entries in frequency domain.
/// with explicit expressions. It also avoids, due to the form of our matrix in the frequency
/// domain, divisions by 2 and repeated modular reductions. This is because of our explicit choice
/// of an MDS matrix that has small powers of 2 entries in frequency domain.
/// The following implementation has benefited greatly from the discussions and insights of
/// Hamish Ivey-Law and Jacqueline Nabaglo of Polygon Zero and is base on Nabaglo's Plonky2
/// implementation.
@@ -19,8 +19,9 @@
// the MDS matrix i.e. just before the multiplication with the appropriate twiddle factors
// and application of the final four 3-point FFT in order to get the full 12-point FFT.
// The entries have been scaled appropriately in order to avoid divisions by 2 in iFFT2 and iFFT4.
// The code to generate the matrix in frequency domain is based on an adaptation of a code, to generate
// MDS matrices efficiently in original domain, that was developed by the Polygon Zero team.
// The code to generate the matrix in frequency domain is based on an adaptation of a code, to
// generate MDS matrices efficiently in original domain, that was developed by the Polygon Zero
// team.
const MDS_FREQ_BLOCK_ONE: [i64; 3] = [16, 8, 16];
const MDS_FREQ_BLOCK_TWO: [(i64, i64); 3] = [(-1, 2), (-1, 1), (4, 8)];
const MDS_FREQ_BLOCK_THREE: [i64; 3] = [-8, 1, 1];

View File

@@ -459,6 +459,7 @@ impl IntoIterator for RpoDigest {
#[cfg(test)]
mod tests {
use alloc::string::String;
use rand_utils::rand_value;
use super::{Deserializable, Felt, RpoDigest, Serializable, DIGEST_BYTES, DIGEST_SIZE};

View File

@@ -152,11 +152,10 @@ impl Hasher for Rpo256 {
fn merge_with_int(seed: Self::Digest, value: u64) -> Self::Digest {
// initialize the state as follows:
// - seed is copied into the first 4 elements of the rate portion of the state.
// - if the value fits into a single field element, copy it into the fifth rate element
// and set the sixth rate element to 1.
// - if the value doesn't fit into a single field element, split it into two field
// elements, copy them into rate elements 5 and 6, and set the seventh rate element
// to 1.
// - if the value fits into a single field element, copy it into the fifth rate element and
// set the sixth rate element to 1.
// - if the value doesn't fit into a single field element, split it into two field elements,
// copy them into rate elements 5 and 6, and set the seventh rate element to 1.
// - set the first capacity element to 1
let mut state = [ZERO; STATE_WIDTH];
state[INPUT1_RANGE].copy_from_slice(seed.as_elements());

View File

@@ -1,3 +1,5 @@
use alloc::{collections::BTreeSet, vec::Vec};
use proptest::prelude::*;
use rand_utils::rand_value;
@@ -6,7 +8,6 @@ use super::{
Felt, FieldElement, Hasher, Rpo256, RpoDigest, StarkField, ONE, STATE_WIDTH, ZERO,
};
use crate::Word;
use alloc::{collections::BTreeSet, vec::Vec};
#[test]
fn test_sbox() {
@@ -58,7 +59,8 @@ fn merge_vs_merge_in_domain() {
];
let merge_result = Rpo256::merge(&digests);
// ------------- merge with domain = 0 ----------------------------------------------------------
// ------------- merge with domain = 0
// ----------------------------------------------------------
// set domain to ZERO. This should not change the result.
let domain = ZERO;
@@ -66,7 +68,8 @@ fn merge_vs_merge_in_domain() {
let merge_in_domain_result = Rpo256::merge_in_domain(&digests, domain);
assert_eq!(merge_result, merge_in_domain_result);
// ------------- merge with domain = 1 ----------------------------------------------------------
// ------------- merge with domain = 1
// ----------------------------------------------------------
// set domain to ONE. This should change the result.
let domain = ONE;

View File

@@ -459,6 +459,7 @@ impl IntoIterator for RpxDigest {
#[cfg(test)]
mod tests {
use alloc::string::String;
use rand_utils::rand_value;
use super::{Deserializable, Felt, RpxDigest, Serializable, DIGEST_BYTES, DIGEST_SIZE};

View File

@@ -26,8 +26,10 @@ pub type CubicExtElement = CubeExtension<Felt>;
/// * Capacity size: 4 field elements.
/// * S-Box degree: 7.
/// * Rounds: There are 3 different types of rounds:
/// - (FB): `apply_mds` → `add_constants` → `apply_sbox` → `apply_mds` → `add_constants` → `apply_inv_sbox`.
/// - (E): `add_constants` → `ext_sbox` (which is raising to power 7 in the degree 3 extension field).
/// - (FB): `apply_mds` → `add_constants` → `apply_sbox` → `apply_mds` → `add_constants` →
/// `apply_inv_sbox`.
/// - (E): `add_constants` → `ext_sbox` (which is raising to power 7 in the degree 3 extension
/// field).
/// - (M): `apply_mds` → `add_constants`.
/// * Permutation: (FB) (E) (FB) (E) (FB) (E) (M).
///
@@ -158,8 +160,8 @@ impl Hasher for Rpx256 {
// - seed is copied into the first 4 elements of the rate portion of the state.
// - if the value fits into a single field element, copy it into the fifth rate element and
// set the first capacity element to 5.
// - if the value doesn't fit into a single field element, split it into two field
// elements, copy them into rate elements 5 and 6 and set the first capacity element to 6.
// - if the value doesn't fit into a single field element, split it into two field elements,
// copy them into rate elements 5 and 6 and set the first capacity element to 6.
let mut state = [ZERO; STATE_WIDTH];
state[INPUT1_RANGE].copy_from_slice(seed.as_elements());
state[INPUT2_RANGE.start] = Felt::new(value);

View File

@@ -33,22 +33,22 @@ impl fmt::Display for MerkleError {
DuplicateValuesForKey(key) => write!(f, "multiple values provided for key {key}"),
InvalidIndex { depth, value } => {
write!(f, "the index value {value} is not valid for the depth {depth}")
}
},
InvalidDepth { expected, provided } => {
write!(f, "the provided depth {provided} is not valid for {expected}")
}
},
InvalidSubtreeDepth { subtree_depth, tree_depth } => {
write!(f, "tried inserting a subtree of depth {subtree_depth} into a tree of depth {tree_depth}")
}
},
InvalidPath(_path) => write!(f, "the provided path is not valid"),
InvalidNumEntries(max) => write!(f, "number of entries exceeded the maximum: {max}"),
NodeNotInSet(index) => write!(f, "the node with index ({index}) is not in the set"),
NodeNotInStore(hash, index) => {
write!(f, "the node {hash:?} with index ({index}) is not in the store")
}
},
NumLeavesNotPowerOfTwo(leaves) => {
write!(f, "the leaves count {leaves} is not a power of 2")
}
},
RootNotInStore(root) => write!(f, "the root {:?} is not in the store", root),
SmtLeaf(smt_leaf_error) => write!(f, "smt leaf error: {smt_leaf_error}"),
}

View File

@@ -1,6 +1,7 @@
use super::super::RpoDigest;
use alloc::vec::Vec;
use super::super::RpoDigest;
/// Container for the update data of a [super::PartialMmr]
#[derive(Debug)]
pub struct MmrDelta {

View File

@@ -21,11 +21,11 @@ impl Display for MmrError {
MmrError::InvalidPeaks => write!(fmt, "Invalid peaks count"),
MmrError::InvalidPeak => {
write!(fmt, "Peak values does not match merkle path computed root")
}
},
MmrError::InvalidUpdate => write!(fmt, "Invalid mmr update"),
MmrError::UnknownPeak => {
write!(fmt, "Peak not in Mmr")
}
},
MmrError::MerkleError(err) => write!(fmt, "{}", err),
}
}

View File

@@ -10,13 +10,14 @@
//! depths, i.e. as part of adding adding a new element to the forest the trees with same depth are
//! merged, creating a new tree with depth d+1, this process is continued until the property is
//! reestablished.
use alloc::vec::Vec;
use super::{
super::{InnerNodeInfo, MerklePath},
bit::TrueBitPositionIterator,
leaf_to_corresponding_tree, nodes_in_forest, MmrDelta, MmrError, MmrPeaks, MmrProof, Rpo256,
RpoDigest,
};
use alloc::vec::Vec;
// MMR
// ===============================================================================================
@@ -377,7 +378,8 @@ impl<'a> Iterator for MmrNodes<'a> {
// the next parent position is one above the position of the pair
let parent = self.last_right << 1;
// the left node has been paired and the current parent yielded, removed it from the forest
// the left node has been paired and the current parent yielded, removed it from the
// forest
self.forest ^= self.last_right;
if self.forest & parent == 0 {
// this iteration yielded the left parent node

View File

@@ -10,8 +10,6 @@ mod proof;
#[cfg(test)]
mod tests;
use super::{Felt, Rpo256, RpoDigest, Word};
// REEXPORTS
// ================================================================================================
pub use delta::MmrDelta;
@@ -22,6 +20,8 @@ pub use partial::PartialMmr;
pub use peaks::MmrPeaks;
pub use proof::MmrProof;
use super::{Felt, Rpo256, RpoDigest, Word};
// UTILITIES
// ===============================================================================================
@@ -42,8 +42,8 @@ const fn leaf_to_corresponding_tree(pos: usize, forest: usize) -> Option<u32> {
// - this means the first tree owns from `0` up to the `2^k_0` first positions, where `k_0`
// is the highest true bit position, the second tree from `2^k_0 + 1` up to `2^k_1` where
// `k_1` is the second highest bit, so on.
// - this means the highest bits work as a category marker, and the position is owned by
// the first tree which doesn't share a high bit with the position
// - this means the highest bits work as a category marker, and the position is owned by the
// first tree which doesn't share a high bit with the position
let before = forest & pos;
let after = forest ^ before;
let tree = after.ilog2();

View File

@@ -1,12 +1,13 @@
use alloc::{
collections::{BTreeMap, BTreeSet},
vec::Vec,
};
use super::{MmrDelta, MmrProof, Rpo256, RpoDigest};
use crate::merkle::{
mmr::{leaf_to_corresponding_tree, nodes_in_forest},
InOrderIndex, InnerNodeInfo, MerklePath, MmrError, MmrPeaks,
};
use alloc::{
collections::{BTreeMap, BTreeSet},
vec::Vec,
};
// TYPE ALIASES
// ================================================================================================
@@ -613,12 +614,13 @@ fn forest_to_rightmost_index(forest: usize) -> InOrderIndex {
#[cfg(test)]
mod tests {
use alloc::{collections::BTreeSet, vec::Vec};
use super::{
forest_to_rightmost_index, forest_to_root_index, InOrderIndex, MmrPeaks, PartialMmr,
RpoDigest,
};
use crate::merkle::{int_to_node, MerkleStore, Mmr, NodeIndex};
use alloc::{collections::BTreeSet, vec::Vec};
const LEAVES: [RpoDigest; 7] = [
int_to_node(0),

View File

@@ -1,6 +1,7 @@
use super::{super::ZERO, Felt, MmrError, MmrProof, Rpo256, RpoDigest, Word};
use alloc::vec::Vec;
use super::{super::ZERO, Felt, MmrError, MmrProof, Rpo256, RpoDigest, Word};
// MMR PEAKS
// ================================================================================================
@@ -18,12 +19,12 @@ pub struct MmrPeaks {
///
/// Examples:
///
/// - With 5 leaves, the binary `0b101`. The number of set bits is equal the number
/// of peaks, in this case there are 2 peaks. The 0-indexed least-significant position of
/// the bit determines the number of elements of a tree, so the rightmost tree has `2**0`
/// - With 5 leaves, the binary `0b101`. The number of set bits is equal the number of
/// peaks, in this case there are 2 peaks. The 0-indexed least-significant position of the
/// bit determines the number of elements of a tree, so the rightmost tree has `2**0`
/// elements and the left most has `2**2`.
/// - With 12 leaves, the binary is `0b1100`, this case also has 2 peaks, the
/// leftmost tree has `2**3=8` elements, and the right most has `2**2=4` elements.
/// - With 12 leaves, the binary is `0b1100`, this case also has 2 peaks, the leftmost tree
/// has `2**3=8` elements, and the right most has `2**2=4` elements.
num_leaves: usize,
/// All the peaks of every tree in the MMR forest. The peaks are always ordered by number of
@@ -94,16 +95,15 @@ impl MmrPeaks {
/// - Flatten the vector of Words into a vector of Felts.
/// - Pad the peaks with ZERO to an even number of words, this removes the need to handle RPO
/// padding.
/// - Pad the peaks to a minimum length of 16 words, which reduces the constant cost of
/// hashing.
/// - Pad the peaks to a minimum length of 16 words, which reduces the constant cost of hashing.
pub fn flatten_and_pad_peaks(&self) -> Vec<Felt> {
let num_peaks = self.peaks.len();
// To achieve the padding rules above we calculate the length of the final vector.
// This is calculated as the number of field elements. Each peak is 4 field elements.
// The length is calculated as follows:
// - If there are less than 16 peaks, the data is padded to 16 peaks and as such requires
// 64 field elements.
// - If there are less than 16 peaks, the data is padded to 16 peaks and as such requires 64
// field elements.
// - If there are more than 16 peaks and the number of peaks is odd, the data is padded to
// an even number of peaks and as such requires `(num_peaks + 1) * 4` field elements.
// - If there are more than 16 peaks and the number of peaks is even, the data is not padded

View File

@@ -1,3 +1,5 @@
use alloc::vec::Vec;
use super::{
super::{InnerNodeInfo, Rpo256, RpoDigest},
bit::TrueBitPositionIterator,
@@ -8,7 +10,6 @@ use crate::{
merkle::{int_to_node, InOrderIndex, MerklePath, MerkleTree, MmrProof, NodeIndex},
Felt, Word,
};
use alloc::vec::Vec;
#[test]
fn test_position_equal_or_higher_than_leafs_is_never_contained() {

View File

@@ -1,3 +1,5 @@
use alloc::{collections::BTreeMap, vec::Vec};
use super::{
super::{
digests_to_words, int_to_node, DefaultMerkleStore as MerkleStore, MerkleTree, NodeIndex,
@@ -5,7 +7,6 @@ use super::{
},
Deserializable, InnerNodeInfo, RpoDigest, Serializable, ValuePath,
};
use alloc::{collections::BTreeMap, vec::Vec};
// TEST DATA
// ================================================================================================
@@ -294,7 +295,8 @@ fn leaves() {
assert!(expected_leaves.eq(pmt.leaves()));
}
/// Checks that nodes of the PMT returned by `inner_nodes()` function are equal to the expected ones.
/// Checks that nodes of the PMT returned by `inner_nodes()` function are equal to the expected
/// ones.
#[test]
fn test_inner_node_iterator() {
let mt = MerkleTree::new(digests_to_words(&VALUES8)).unwrap();

View File

@@ -37,17 +37,17 @@ impl fmt::Display for SmtLeafError {
match self {
InvalidNumEntriesForMultiple(num_entries) => {
write!(f, "Multiple leaf requires 2 or more entries. Got: {num_entries}")
}
},
InconsistentKeys { entries, key_1, key_2 } => {
write!(f, "Multiple leaf requires all keys to map to the same leaf index. Offending keys: {key_1} and {key_2}. Entries: {entries:?}.")
}
},
SingleKeyInconsistentWithLeafIndex { key, leaf_index } => {
write!(
f,
"Single key in leaf inconsistent with leaf index. Key: {key}, leaf index: {}",
leaf_index.value()
)
}
},
MultipleKeysInconsistentWithLeafIndex {
leaf_index_from_keys,
leaf_index_supplied,
@@ -58,7 +58,7 @@ impl fmt::Display for SmtLeafError {
leaf_index_from_keys.value(),
leaf_index_supplied.value()
)
}
},
}
}
}
@@ -80,7 +80,7 @@ impl fmt::Display for SmtProofError {
match self {
InvalidPathLength(path_length) => {
write!(f, "Invalid Merkle path length. Expected {SMT_DEPTH}, got {path_length}")
}
},
}
}
}

View File

@@ -20,8 +20,8 @@ impl SmtLeaf {
///
/// # Errors
/// - Returns an error if 2 keys in `entries` map to a different leaf index
/// - Returns an error if 1 or more keys in `entries` map to a leaf index
/// different from `leaf_index`
/// - Returns an error if 1 or more keys in `entries` map to a leaf index different from
/// `leaf_index`
pub fn new(
entries: Vec<(RpoDigest, Word)>,
leaf_index: LeafIndex<SMT_DEPTH>,
@@ -39,7 +39,7 @@ impl SmtLeaf {
}
Ok(Self::new_single(key, value))
}
},
_ => {
let leaf = Self::new_multiple(entries)?;
@@ -53,7 +53,7 @@ impl SmtLeaf {
} else {
Ok(leaf)
}
}
},
}
}
@@ -118,7 +118,7 @@ impl SmtLeaf {
// Note: All keys are guaranteed to have the same leaf index
let (first_key, _) = entries[0];
first_key.into()
}
},
}
}
@@ -129,7 +129,7 @@ impl SmtLeaf {
SmtLeaf::Single(_) => 1,
SmtLeaf::Multiple(entries) => {
entries.len().try_into().expect("shouldn't have more than 2^64 entries")
}
},
}
}
@@ -141,7 +141,7 @@ impl SmtLeaf {
SmtLeaf::Multiple(kvs) => {
let elements: Vec<Felt> = kvs.iter().copied().flat_map(kv_to_elements).collect();
Rpo256::hash_elements(&elements)
}
},
}
}
@@ -182,7 +182,8 @@ impl SmtLeaf {
// HELPERS
// ---------------------------------------------------------------------------------------------
/// Returns the value associated with `key` in the leaf, or `None` if `key` maps to another leaf.
/// Returns the value associated with `key` in the leaf, or `None` if `key` maps to another
/// leaf.
pub(super) fn get_value(&self, key: &RpoDigest) -> Option<Word> {
// Ensure that `key` maps to this leaf
if self.index() != key.into() {
@@ -197,7 +198,7 @@ impl SmtLeaf {
} else {
Some(EMPTY_WORD)
}
}
},
SmtLeaf::Multiple(kv_pairs) => {
for (key_in_leaf, value_in_leaf) in kv_pairs {
if key == key_in_leaf {
@@ -206,7 +207,7 @@ impl SmtLeaf {
}
Some(EMPTY_WORD)
}
},
}
}
@@ -219,7 +220,7 @@ impl SmtLeaf {
SmtLeaf::Empty(_) => {
*self = SmtLeaf::new_single(key, value);
None
}
},
SmtLeaf::Single(kv_pair) => {
if kv_pair.0 == key {
// the key is already in this leaf. Update the value and return the previous
@@ -237,7 +238,7 @@ impl SmtLeaf {
None
}
}
},
SmtLeaf::Multiple(kv_pairs) => {
match kv_pairs.binary_search_by(|kv_pair| cmp_keys(kv_pair.0, key)) {
Ok(pos) => {
@@ -245,14 +246,14 @@ impl SmtLeaf {
kv_pairs[pos].1 = value;
Some(old_value)
}
},
Err(pos) => {
kv_pairs.insert(pos, (key, value));
None
}
},
}
}
},
}
}
@@ -277,7 +278,7 @@ impl SmtLeaf {
// another key is stored at leaf; nothing to update
(None, false)
}
}
},
SmtLeaf::Multiple(kv_pairs) => {
match kv_pairs.binary_search_by(|kv_pair| cmp_keys(kv_pair.0, key)) {
Ok(pos) => {
@@ -292,13 +293,13 @@ impl SmtLeaf {
}
(Some(old_value), false)
}
},
Err(_) => {
// other keys are stored at leaf; nothing to update
(None, false)
}
},
}
}
},
}
}
}

View File

@@ -1,13 +1,14 @@
use super::{
EmptySubtreeRoots, Felt, InnerNode, InnerNodeInfo, LeafIndex, MerkleError, MerklePath,
NodeIndex, Rpo256, RpoDigest, SparseMerkleTree, Word, EMPTY_WORD,
};
use alloc::{
collections::{BTreeMap, BTreeSet},
string::ToString,
vec::Vec,
};
use super::{
EmptySubtreeRoots, Felt, InnerNode, InnerNodeInfo, LeafIndex, MerkleError, MerklePath,
NodeIndex, Rpo256, RpoDigest, SparseMerkleTree, Word, EMPTY_WORD,
};
mod error;
pub use error::{SmtLeafError, SmtProofError};
@@ -32,8 +33,8 @@ pub const SMT_DEPTH: u8 = 64;
/// Sparse Merkle tree mapping 256-bit keys to 256-bit values. Both keys and values are represented
/// by 4 field elements.
///
/// All leaves sit at depth 64. The most significant element of the key is used to identify the leaf to
/// which the key maps.
/// All leaves sit at depth 64. The most significant element of the key is used to identify the leaf
/// to which the key maps.
///
/// A leaf is either empty, or holds one or more key-value pairs. An empty leaf hashes to the empty
/// word. Otherwise, a leaf hashes to the hash of its key-value pairs, ordered by key first, value
@@ -187,7 +188,7 @@ impl Smt {
self.leaves.insert(leaf_index.value(), SmtLeaf::Single((key, value)));
None
}
},
}
}

View File

@@ -1,6 +1,7 @@
use alloc::string::ToString;
use super::{MerklePath, RpoDigest, SmtLeaf, SmtProofError, Word, SMT_DEPTH};
use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
use alloc::string::ToString;
/// A proof which can be used to assert membership (or non-membership) of key-value pairs in a
/// [`super::Smt`].
@@ -57,7 +58,7 @@ impl SmtProof {
// make sure the Merkle path resolves to the correct root
self.compute_root() == *root
}
},
// If the key maps to a different leaf, the proof cannot verify membership of `value`
None => false,
}

View File

@@ -1,10 +1,11 @@
use alloc::vec::Vec;
use super::{Felt, LeafIndex, NodeIndex, Rpo256, RpoDigest, Smt, SmtLeaf, EMPTY_WORD, SMT_DEPTH};
use crate::{
merkle::{EmptySubtreeRoots, MerkleStore},
utils::{Deserializable, Serializable},
Word, ONE, WORD_SIZE,
};
use alloc::vec::Vec;
// SMT
// --------------------------------------------------------------------------------------------

View File

@@ -1,9 +1,10 @@
use alloc::vec::Vec;
use super::{EmptySubtreeRoots, InnerNodeInfo, MerkleError, MerklePath, NodeIndex};
use crate::{
hash::rpo::{Rpo256, RpoDigest},
Felt, Word, EMPTY_WORD,
};
use alloc::vec::Vec;
mod full;
pub use full::{Smt, SmtLeaf, SmtLeafError, SmtProof, SmtProofError, SMT_DEPTH};

View File

@@ -1,9 +1,10 @@
use alloc::collections::{BTreeMap, BTreeSet};
use super::{
super::ValuePath, EmptySubtreeRoots, InnerNode, InnerNodeInfo, LeafIndex, MerkleError,
MerklePath, NodeIndex, RpoDigest, SparseMerkleTree, Word, EMPTY_WORD, SMT_MAX_DEPTH,
SMT_MIN_DEPTH,
};
use alloc::collections::{BTreeMap, BTreeSet};
#[cfg(test)]
mod tests;

View File

@@ -1,3 +1,5 @@
use alloc::vec::Vec;
use super::{
super::{MerkleError, RpoDigest, SimpleSmt},
NodeIndex,
@@ -10,7 +12,6 @@ use crate::{
},
Word, EMPTY_WORD,
};
use alloc::vec::Vec;
// TEST DATA
// ================================================================================================

View File

@@ -127,8 +127,8 @@ impl<T: KvMap<RpoDigest, StoreNode>> MerkleStore<T> {
/// # Errors
/// This method can return the following errors:
/// - `RootNotInStore` if the `root` is not present in the store.
/// - `NodeNotInStore` if a node needed to traverse from `root` to `index` is not present in
/// the store.
/// - `NodeNotInStore` if a node needed to traverse from `root` to `index` is not present in the
/// store.
pub fn get_node(&self, root: RpoDigest, index: NodeIndex) -> Result<RpoDigest, MerkleError> {
let mut hash = root;
@@ -152,8 +152,8 @@ impl<T: KvMap<RpoDigest, StoreNode>> MerkleStore<T> {
/// # Errors
/// This method can return the following errors:
/// - `RootNotInStore` if the `root` is not present in the store.
/// - `NodeNotInStore` if a node needed to traverse from `root` to `index` is not present in
/// the store.
/// - `NodeNotInStore` if a node needed to traverse from `root` to `index` is not present in the
/// store.
pub fn get_path(&self, root: RpoDigest, index: NodeIndex) -> Result<ValuePath, MerkleError> {
let mut hash = root;
let mut path = Vec::with_capacity(index.depth().into());
@@ -421,8 +421,8 @@ impl<T: KvMap<RpoDigest, StoreNode>> MerkleStore<T> {
/// # Errors
/// This method can return the following errors:
/// - `RootNotInStore` if the `root` is not present in the store.
/// - `NodeNotInStore` if a node needed to traverse from `root` to `index` is not present in
/// the store.
/// - `NodeNotInStore` if a node needed to traverse from `root` to `index` is not present in the
/// store.
pub fn set_node(
&mut self,
mut root: RpoDigest,

View File

@@ -1,4 +1,10 @@
use seq_macro::seq;
#[cfg(feature = "std")]
use {
super::{Deserializable, Serializable},
alloc::boxed::Box,
std::error::Error,
};
use super::{
DefaultMerkleStore as MerkleStore, EmptySubtreeRoots, MerkleError, MerklePath, NodeIndex,
@@ -11,13 +17,6 @@ use crate::{
Felt, Word, ONE, WORD_SIZE, ZERO,
};
#[cfg(feature = "std")]
use {
super::{Deserializable, Serializable},
alloc::boxed::Box,
std::error::Error,
};
// TEST DATA
// ================================================================================================

View File

@@ -1,10 +1,12 @@
use alloc::{string::ToString, vec::Vec};
use rand_core::impls;
use super::{Felt, FeltRng, FieldElement, RandomCoin, RandomCoinError, RngCore, Word, ZERO};
use crate::{
hash::rpo::{Rpo256, RpoDigest},
utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
};
use alloc::{string::ToString, vec::Vec};
use rand_core::impls;
// CONSTANTS
// ================================================================================================
@@ -20,8 +22,8 @@ const HALF_RATE_WIDTH: usize = (Rpo256::RATE_RANGE.end - Rpo256::RATE_RANGE.star
/// described in <https://eprint.iacr.org/2011/499.pdf>.
///
/// The simplification is related to the following facts:
/// 1. A call to the reseed method implies one and only one call to the permutation function.
/// This is possible because in our case we never reseed with more than 4 field elements.
/// 1. A call to the reseed method implies one and only one call to the permutation function. This
/// is possible because in our case we never reseed with more than 4 field elements.
/// 2. As a result of the previous point, we don't make use of an input buffer to accumulate seed
/// material.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]

View File

@@ -1,10 +1,12 @@
use alloc::{string::ToString, vec::Vec};
use rand_core::impls;
use super::{Felt, FeltRng, FieldElement, RandomCoin, RandomCoinError, RngCore, Word, ZERO};
use crate::{
hash::rpx::{Rpx256, RpxDigest},
utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
};
use alloc::{string::ToString, vec::Vec};
use rand_core::impls;
// CONSTANTS
// ================================================================================================
@@ -20,8 +22,8 @@ const HALF_RATE_WIDTH: usize = (Rpx256::RATE_RANGE.end - Rpx256::RATE_RANGE.star
/// described in <https://eprint.iacr.org/2011/499.pdf>.
///
/// The simplification is related to the following facts:
/// 1. A call to the reseed method implies one and only one call to the permutation function.
/// This is possible because in our case we never reseed with more than 4 field elements.
/// 1. A call to the reseed method implies one and only one call to the permutation function. This
/// is possible because in our case we never reseed with more than 4 field elements.
/// 2. As a result of the previous point, we don't make use of an input buffer to accumulate seed
/// material.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]

View File

@@ -62,8 +62,8 @@ impl<K: Ord + Clone, V: Clone> KvMap<K, V> for BTreeMap<K, V> {
/// The [RecordingMap] is composed of three parts:
/// - `data`: which contains the current set of key-value pairs in the map.
/// - `updates`: which tracks keys for which values have been changed since the map was
/// instantiated. updates include both insertions, removals and updates of values under existing
/// keys.
/// instantiated. updates include both insertions, removals and updates of values under existing
/// keys.
/// - `trace`: which contains the key-value pairs from the original data which have been accesses
/// since the map was instantiated.
#[derive(Debug, Default, Clone, Eq, PartialEq)]
@@ -325,7 +325,8 @@ mod tests {
let mut map = RecordingMap::new(ITEMS.to_vec());
assert!(map.iter().all(|(x, y)| ITEMS.contains(&(*x, *y))));
// when inserting entry with key that already exists the iterator should return the new value
// when inserting entry with key that already exists the iterator should return the new
// value
let new_value = 5;
map.insert(4, new_value);
assert_eq!(map.iter().count(), ITEMS.len());

View File

@@ -59,16 +59,16 @@ impl Display for HexParseError {
match self {
HexParseError::InvalidLength { expected, actual } => {
write!(f, "Expected hex data to have length {expected}, including the 0x prefix. Got {actual}")
}
},
HexParseError::MissingPrefix => {
write!(f, "Hex encoded data must start with 0x prefix")
}
},
HexParseError::InvalidChar => {
write!(f, "Hex encoded data must contain characters [a-zA-Z0-9]")
}
},
HexParseError::OutOfRange => {
write!(f, "Hex encoded values of an RpoDigest must be inside the field modulus")
}
},
}
}
}