Update to frozen spec ❄️ (v0.8.1) (#444)

* types: first updates for v0.8

* state_processing: epoch processing v0.8.0

* state_processing: block processing v0.8.0

* tree_hash_derive: support generics in SignedRoot

* types v0.8: update to use ssz_types

* state_processing v0.8: use ssz_types

* ssz_types: add bitwise methods and from_elem

* types: fix v0.8 FIXMEs

* ssz_types: add bitfield shift_up

* ssz_types: iterators and DerefMut for VariableList

* types,state_processing: use VariableList

* ssz_types: fix BitVector Decode impl

Fixed a typo in the implementation of ssz::Decode for BitVector, which caused it
to be considered variable length!

* types: fix test modules for v0.8 update

* types: remove slow type-level arithmetic

* state_processing: fix tests for v0.8

* op_pool: update for v0.8

* ssz_types: Bitfield difference length-independent

Allow computing the difference of two bitfields of different lengths.

* Implement compact committee support

* epoch_processing: committee & active index roots

* state_processing: genesis state builder v0.8

* state_processing: implement v0.8.1

* Further improve tree_hash

* Strip examples, tests from cached_tree_hash

* Update TreeHash, un-impl CachedTreeHash

* Update bitfield TreeHash, un-impl CachedTreeHash

* Update FixedLenVec TreeHash, unimpl CachedTreeHash

* Update update tree_hash_derive for new TreeHash

* Fix TreeHash, un-impl CachedTreeHash for ssz_types

* Remove fixed_len_vec, ssz benches

SSZ benches relied upon fixed_len_vec -- it is easier to just delete
them and rebuild them later (when necessary)

* Remove boolean_bitfield crate

* Fix fake_crypto BLS compile errors

* Update ef_tests for new v.8 type params

* Update ef_tests submodule to v0.8.1 tag

* Make fixes to support parsing ssz ef_tests

* `compact_committee...` to `compact_committees...`

* Derive more traits for `CompactCommittee`

* Flip bitfield byte-endianness

* Fix tree_hash for bitfields

* Modify CLI output for ef_tests

* Bump ssz crate version

* Update ssz_types doc comment

* Del cached tree hash tests from ssz_static tests

* Tidy SSZ dependencies

* Rename ssz_types crate to eth2_ssz_types

* validator_client: update for v0.8

* ssz_types: update union/difference for bit order swap

* beacon_node: update for v0.8, EthSpec

* types: disable cached tree hash, update min spec

* state_processing: fix slot bug in committee update

* tests: temporarily disable fork choice harness test

See #447

* committee cache: prevent out-of-bounds access

In the case where we tried to access the committee of a shard that didn't have a committee in the
current epoch, we were accessing elements beyond the end of the shuffling vector and panicking! This
commit adds a check to make the failure safe and explicit.

* fix bug in get_indexed_attestation and simplify

There was a bug in our implementation of get_indexed_attestation whereby
incorrect "committee indices" were used to index into the custody bitfield. The
bug was only observable in the case where some bits of the custody bitfield were
set to 1. The implementation has been simplified to remove the bug, and a test
added.

* state_proc: workaround for compact committees bug

https://github.com/ethereum/eth2.0-specs/issues/1315

* v0.8: updates to make the EF tests pass

* Remove redundant max operation checks.
* Always supply both messages when checking attestation signatures -- allowing
  verification of an attestation with no signatures.
* Swap the order of the fork and domain constant in `get_domain`, to match
  the spec.

* rustfmt

* ef_tests: add new epoch processing tests

* Integrate v0.8 into master (compiles)

* Remove unused crates, fix clippy lints

* Replace v0.6.3 tags w/ v0.8.1

* Remove old comment

* Ensure lmd ghost tests only run in release

* Update readme
This commit is contained in:
Michael Sproul
2019-07-30 12:44:51 +10:00
committed by Paul Hauner
parent 177df12149
commit a236003a7b
184 changed files with 3332 additions and 4542 deletions
+1
View File
@@ -14,6 +14,7 @@ serde = "1.0"
serde_derive = "1.0"
serde_hex = { path = "../serde_hex" }
eth2_ssz = { path = "../ssz" }
eth2_ssz_types = { path = "../ssz_types" }
tree_hash = { path = "../tree_hash" }
[features]
+5 -6
View File
@@ -1,13 +1,11 @@
use super::*;
use cached_tree_hash::cached_tree_hash_ssz_encoding_as_vector;
use milagro_bls::{
AggregatePublicKey as RawAggregatePublicKey, AggregateSignature as RawAggregateSignature,
};
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use serde_hex::{encode as hex_encode, HexVisitor};
use ssz::{Decode, DecodeError};
use tree_hash::tree_hash_ssz_encoding_as_vector;
use ssz::{Decode, DecodeError, Encode};
/// A BLS aggregate signature.
///
@@ -143,6 +141,10 @@ impl_ssz!(
"AggregateSignature"
);
impl_tree_hash!(AggregateSignature, U96);
impl_cached_tree_hash!(AggregateSignature, U96);
impl Serialize for AggregateSignature {
/// Serde serialization is compliant the Ethereum YAML test format.
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@@ -167,9 +169,6 @@ impl<'de> Deserialize<'de> for AggregateSignature {
}
}
tree_hash_ssz_encoding_as_vector!(AggregateSignature);
cached_tree_hash_ssz_encoding_as_vector!(AggregateSignature, 96);
#[cfg(test)]
mod tests {
use super::super::{Keypair, Signature};
@@ -2,12 +2,10 @@ use super::{
fake_aggregate_public_key::FakeAggregatePublicKey, fake_signature::FakeSignature,
BLS_AGG_SIG_BYTE_SIZE,
};
use cached_tree_hash::cached_tree_hash_ssz_encoding_as_vector;
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use serde_hex::{encode as hex_encode, PrefixedHexVisitor};
use ssz::{ssz_encode, Decode, DecodeError};
use tree_hash::tree_hash_ssz_encoding_as_vector;
use ssz::{ssz_encode, Decode, DecodeError, Encode};
/// A BLS aggregate signature.
///
@@ -86,6 +84,10 @@ impl_ssz!(
"FakeAggregateSignature"
);
impl_tree_hash!(FakeAggregateSignature, U96);
impl_cached_tree_hash!(FakeAggregateSignature, U96);
impl Serialize for FakeAggregateSignature {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
@@ -107,9 +109,6 @@ impl<'de> Deserialize<'de> for FakeAggregateSignature {
}
}
tree_hash_ssz_encoding_as_vector!(FakeAggregateSignature);
cached_tree_hash_ssz_encoding_as_vector!(FakeAggregateSignature, 96);
#[cfg(test)]
mod tests {
use super::super::{Keypair, Signature};
+5 -6
View File
@@ -1,13 +1,11 @@
use super::{SecretKey, BLS_PUBLIC_KEY_BYTE_SIZE};
use cached_tree_hash::cached_tree_hash_ssz_encoding_as_vector;
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use serde_hex::{encode as hex_encode, HexVisitor};
use ssz::{ssz_encode, Decode, DecodeError};
use ssz::{ssz_encode, Decode, DecodeError, Encode};
use std::default;
use std::fmt;
use std::hash::{Hash, Hasher};
use tree_hash::tree_hash_ssz_encoding_as_vector;
/// A single BLS signature.
///
@@ -84,6 +82,10 @@ impl default::Default for FakePublicKey {
impl_ssz!(FakePublicKey, BLS_PUBLIC_KEY_BYTE_SIZE, "FakePublicKey");
impl_tree_hash!(FakePublicKey, U48);
impl_cached_tree_hash!(FakePublicKey, U48);
impl Serialize for FakePublicKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
@@ -105,9 +107,6 @@ impl<'de> Deserialize<'de> for FakePublicKey {
}
}
tree_hash_ssz_encoding_as_vector!(FakePublicKey);
cached_tree_hash_ssz_encoding_as_vector!(FakePublicKey, 48);
impl PartialEq for FakePublicKey {
fn eq(&self, other: &FakePublicKey) -> bool {
ssz_encode(self) == ssz_encode(other)
+4 -5
View File
@@ -1,11 +1,9 @@
use super::{PublicKey, SecretKey, BLS_SIG_BYTE_SIZE};
use cached_tree_hash::cached_tree_hash_ssz_encoding_as_vector;
use hex::encode as hex_encode;
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use serde_hex::HexVisitor;
use ssz::{ssz_encode, Decode, DecodeError};
use tree_hash::tree_hash_ssz_encoding_as_vector;
use ssz::{ssz_encode, Decode, DecodeError, Encode};
/// A single BLS signature.
///
@@ -84,8 +82,9 @@ impl FakeSignature {
impl_ssz!(FakeSignature, BLS_SIG_BYTE_SIZE, "FakeSignature");
tree_hash_ssz_encoding_as_vector!(FakeSignature);
cached_tree_hash_ssz_encoding_as_vector!(FakeSignature, 96);
impl_tree_hash!(FakeSignature, U96);
impl_cached_tree_hash!(FakeSignature, U96);
impl Serialize for FakeSignature {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+48
View File
@@ -36,3 +36,51 @@ macro_rules! impl_ssz {
}
};
}
macro_rules! impl_tree_hash {
($type: ty, $byte_size: ident) => {
impl tree_hash::TreeHash for $type {
fn tree_hash_type() -> tree_hash::TreeHashType {
tree_hash::TreeHashType::Vector
}
fn tree_hash_packed_encoding(&self) -> Vec<u8> {
unreachable!("Vector should never be packed.")
}
fn tree_hash_packing_factor() -> usize {
unreachable!("Vector should never be packed.")
}
fn tree_hash_root(&self) -> Vec<u8> {
let vector: ssz_types::FixedVector<u8, ssz_types::typenum::$byte_size> =
ssz_types::FixedVector::from(self.as_ssz_bytes());
vector.tree_hash_root()
}
}
};
}
macro_rules! impl_cached_tree_hash {
($type: ty, $byte_size: ident) => {
impl cached_tree_hash::CachedTreeHash for $type {
fn new_tree_hash_cache(
&self,
_depth: usize,
) -> Result<cached_tree_hash::TreeHashCache, cached_tree_hash::Error> {
unimplemented!("CachedTreeHash is not implemented for BLS types")
}
fn tree_hash_cache_schema(&self, _depth: usize) -> cached_tree_hash::BTreeSchema {
unimplemented!("CachedTreeHash is not implemented for BLS types")
}
fn update_tree_hash_cache(
&self,
_cache: &mut cached_tree_hash::TreeHashCache,
) -> Result<(), cached_tree_hash::Error> {
unimplemented!("CachedTreeHash is not implemented for BLS types")
}
}
};
}
+6 -5
View File
@@ -1,5 +1,4 @@
use super::{SecretKey, BLS_PUBLIC_KEY_BYTE_SIZE};
use cached_tree_hash::cached_tree_hash_ssz_encoding_as_vector;
use milagro_bls::PublicKey as RawPublicKey;
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
@@ -8,7 +7,6 @@ use ssz::{Decode, DecodeError, Encode};
use std::default;
use std::fmt;
use std::hash::{Hash, Hasher};
use tree_hash::tree_hash_ssz_encoding_as_vector;
/// A single BLS signature.
///
@@ -92,6 +90,10 @@ impl default::Default for PublicKey {
impl_ssz!(PublicKey, BLS_PUBLIC_KEY_BYTE_SIZE, "PublicKey");
impl_tree_hash!(PublicKey, U48);
impl_cached_tree_hash!(PublicKey, U48);
impl Serialize for PublicKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
@@ -113,9 +115,6 @@ impl<'de> Deserialize<'de> for PublicKey {
}
}
tree_hash_ssz_encoding_as_vector!(PublicKey);
cached_tree_hash_ssz_encoding_as_vector!(PublicKey, 48);
impl PartialEq for PublicKey {
fn eq(&self, other: &PublicKey) -> bool {
self.as_ssz_bytes() == other.as_ssz_bytes()
@@ -152,6 +151,8 @@ mod tests {
}
#[test]
// TODO: once `CachedTreeHash` is fixed, this test should _not_ panic.
#[should_panic]
pub fn test_cached_tree_hash() {
let sk = SecretKey::random();
let original = PublicKey::from_secret_key(&sk);
+5 -4
View File
@@ -6,8 +6,7 @@ use milagro_bls::SecretKey as RawSecretKey;
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use serde_hex::HexVisitor;
use ssz::{ssz_encode, Decode, DecodeError};
use tree_hash::tree_hash_ssz_encoding_as_vector;
use ssz::{ssz_encode, Decode, DecodeError, Encode};
/// A single BLS signature.
///
@@ -46,6 +45,10 @@ impl SecretKey {
impl_ssz!(SecretKey, BLS_SECRET_KEY_BYTE_SIZE, "SecretKey");
impl_tree_hash!(SecretKey, U48);
impl_cached_tree_hash!(SecretKey, U48);
impl Serialize for SecretKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
@@ -67,8 +70,6 @@ impl<'de> Deserialize<'de> for SecretKey {
}
}
tree_hash_ssz_encoding_as_vector!(SecretKey);
#[cfg(test)]
mod tests {
use super::*;
+6 -5
View File
@@ -1,12 +1,10 @@
use super::{PublicKey, SecretKey, BLS_SIG_BYTE_SIZE};
use cached_tree_hash::cached_tree_hash_ssz_encoding_as_vector;
use hex::encode as hex_encode;
use milagro_bls::Signature as RawSignature;
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use serde_hex::HexVisitor;
use ssz::{ssz_encode, Decode, DecodeError};
use tree_hash::tree_hash_ssz_encoding_as_vector;
use ssz::{ssz_encode, Decode, DecodeError, Encode};
/// A single BLS signature.
///
@@ -111,8 +109,9 @@ impl Signature {
impl_ssz!(Signature, BLS_SIG_BYTE_SIZE, "Signature");
tree_hash_ssz_encoding_as_vector!(Signature);
cached_tree_hash_ssz_encoding_as_vector!(Signature, 96);
impl_tree_hash!(Signature, U96);
impl_cached_tree_hash!(Signature, U96);
impl Serialize for Signature {
/// Serde serialization is compliant the Ethereum YAML test format.
@@ -157,6 +156,8 @@ mod tests {
}
#[test]
// TODO: once `CachedTreeHash` is fixed, this test should _not_ panic.
#[should_panic]
pub fn test_cached_tree_hash() {
let keypair = Keypair::random();
let original = Signature::new(&[42, 42], 0, &keypair.sk);
-17
View File
@@ -1,17 +0,0 @@
[package]
name = "boolean-bitfield"
version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2018"
[dependencies]
cached_tree_hash = { path = "../cached_tree_hash" }
serde_hex = { path = "../serde_hex" }
eth2_ssz = { path = "../ssz" }
bit-vec = "0.5.0"
bit_reverse = "0.1"
serde = "1.0"
tree_hash = { path = "../tree_hash" }
[dev-dependencies]
serde_yaml = "0.8"
-3
View File
@@ -1,3 +0,0 @@
# Boolean Bitfield
Implements a set of boolean as a tightly-packed vector of bits.
@@ -1,4 +0,0 @@
target
corpus
artifacts
@@ -1,33 +0,0 @@
[package]
name = "boolean-bitfield-fuzz"
version = "0.0.1"
authors = ["Automatically generated"]
publish = false
[package.metadata]
cargo-fuzz = true
[dependencies]
eth2_ssz = { path = "../../ssz" }
[dependencies.boolean-bitfield]
path = ".."
[dependencies.libfuzzer-sys]
git = "https://github.com/rust-fuzz/libfuzzer-sys.git"
# Prevent this from interfering with workspaces
[workspace]
members = ["."]
[[bin]]
name = "fuzz_target_from_bytes"
path = "fuzz_targets/fuzz_target_from_bytes.rs"
[[bin]]
name = "fuzz_target_ssz_decode"
path = "fuzz_targets/fuzz_target_ssz_decode.rs"
[[bin]]
name = "fuzz_target_ssz_encode"
path = "fuzz_targets/fuzz_target_ssz_encode.rs"
@@ -1,9 +0,0 @@
#![no_main]
#[macro_use] extern crate libfuzzer_sys;
extern crate boolean_bitfield;
use boolean_bitfield::BooleanBitfield;
fuzz_target!(|data: &[u8]| {
let _result = BooleanBitfield::from_bytes(data);
});
@@ -1,11 +0,0 @@
#![no_main]
#[macro_use] extern crate libfuzzer_sys;
extern crate boolean_bitfield;
extern crate ssz;
use boolean_bitfield::BooleanBitfield;
use ssz::{Decodable, DecodeError};
fuzz_target!(|data: &[u8]| {
let result: Result<(BooleanBitfield, usize), DecodeError> = <_>::ssz_decode(data, 0);
});
@@ -1,13 +0,0 @@
#![no_main]
#[macro_use] extern crate libfuzzer_sys;
extern crate boolean_bitfield;
extern crate ssz;
use boolean_bitfield::BooleanBitfield;
use ssz::SszStream;
fuzz_target!(|data: &[u8]| {
let bitfield = BooleanBitfield::from_bytes(data);
let mut ssz = SszStream::new();
ssz.append(&bitfield);
});
-572
View File
@@ -1,572 +0,0 @@
extern crate bit_vec;
extern crate ssz;
use bit_reverse::LookupReverse;
use bit_vec::BitVec;
use cached_tree_hash::cached_tree_hash_bytes_as_list;
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use serde_hex::{encode, PrefixedHexVisitor};
use ssz::{Decode, Encode};
use std::cmp;
use std::default;
/// A BooleanBitfield represents a set of booleans compactly stored as a vector of bits.
/// The BooleanBitfield is given a fixed size during construction. Reads outside of the current size return an out-of-bounds error. Writes outside of the current size expand the size of the set.
#[derive(Debug, Clone, Hash)]
pub struct BooleanBitfield(BitVec);
/// Error represents some reason a request against a bitfield was not satisfied
#[derive(Debug, PartialEq)]
pub enum Error {
/// OutOfBounds refers to indexing into a bitfield where no bits exist; returns the illegal index and the current size of the bitfield, respectively
OutOfBounds(usize, usize),
}
impl BooleanBitfield {
/// Create a new bitfield.
pub fn new() -> Self {
Default::default()
}
pub fn with_capacity(initial_len: usize) -> Self {
Self::from_elem(initial_len, false)
}
/// Create a new bitfield with the given length `initial_len` and all values set to `bit`.
///
/// Note: if `initial_len` is not a multiple of 8, the remaining bits will be set to `false`
/// regardless of `bit`.
pub fn from_elem(initial_len: usize, bit: bool) -> Self {
// BitVec can panic if we don't set the len to be a multiple of 8.
let full_len = ((initial_len + 7) / 8) * 8;
let mut bitfield = BitVec::from_elem(full_len, false);
if bit {
for i in 0..initial_len {
bitfield.set(i, true);
}
}
Self { 0: bitfield }
}
/// Create a new bitfield using the supplied `bytes` as input
pub fn from_bytes(bytes: &[u8]) -> Self {
Self {
0: BitVec::from_bytes(&reverse_bit_order(bytes.to_vec())),
}
}
/// Returns a vector of bytes representing the bitfield
pub fn to_bytes(&self) -> Vec<u8> {
reverse_bit_order(self.0.to_bytes().to_vec())
}
/// Read the value of a bit.
///
/// If the index is in bounds, then result is Ok(value) where value is `true` if the bit is 1 and `false` if the bit is 0.
/// If the index is out of bounds, we return an error to that extent.
pub fn get(&self, i: usize) -> Result<bool, Error> {
match self.0.get(i) {
Some(value) => Ok(value),
None => Err(Error::OutOfBounds(i, self.0.len())),
}
}
/// Set the value of a bit.
///
/// If the index is out of bounds, we expand the size of the underlying set to include the new index.
/// Returns the previous value if there was one.
pub fn set(&mut self, i: usize, value: bool) -> Option<bool> {
let previous = match self.get(i) {
Ok(previous) => Some(previous),
Err(Error::OutOfBounds(_, len)) => {
let new_len = i - len + 1;
self.0.grow(new_len, false);
None
}
};
self.0.set(i, value);
previous
}
/// Returns the number of bits in this bitfield.
pub fn len(&self) -> usize {
self.0.len()
}
/// Returns true if `self.len() == 0`
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns true if all bits are set to 0.
pub fn is_zero(&self) -> bool {
self.0.none()
}
/// Returns the number of bytes required to represent this bitfield.
pub fn num_bytes(&self) -> usize {
self.to_bytes().len()
}
/// Returns the number of `1` bits in the bitfield
pub fn num_set_bits(&self) -> usize {
self.0.iter().filter(|&bit| bit).count()
}
/// Compute the intersection (binary-and) of this bitfield with another. Lengths must match.
pub fn intersection(&self, other: &Self) -> Self {
let mut res = self.clone();
res.intersection_inplace(other);
res
}
/// Like `intersection` but in-place (updates `self`).
pub fn intersection_inplace(&mut self, other: &Self) {
self.0.intersect(&other.0);
}
/// Compute the union (binary-or) of this bitfield with another. Lengths must match.
pub fn union(&self, other: &Self) -> Self {
let mut res = self.clone();
res.union_inplace(other);
res
}
/// Like `union` but in-place (updates `self`).
pub fn union_inplace(&mut self, other: &Self) {
self.0.union(&other.0);
}
/// Compute the difference (binary-minus) of this bitfield with another. Lengths must match.
///
/// Computes `self - other`.
pub fn difference(&self, other: &Self) -> Self {
let mut res = self.clone();
res.difference_inplace(other);
res
}
/// Like `difference` but in-place (updates `self`).
pub fn difference_inplace(&mut self, other: &Self) {
self.0.difference(&other.0);
}
}
impl default::Default for BooleanBitfield {
/// default provides the "empty" bitfield
/// Note: the empty bitfield is set to the `0` byte.
fn default() -> Self {
Self::from_elem(8, false)
}
}
impl cmp::PartialEq for BooleanBitfield {
/// Determines equality by comparing the `ssz` encoding of the two candidates.
/// This method ensures that the presence of high-order (empty) bits in the highest byte do not exclude equality when they are in fact representing the same information.
fn eq(&self, other: &Self) -> bool {
ssz::ssz_encode(self) == ssz::ssz_encode(other)
}
}
impl Eq for BooleanBitfield {}
/// Create a new bitfield that is a union of two other bitfields.
///
/// For example `union(0101, 1000) == 1101`
// TODO: length-independent intersection for BitAnd
impl std::ops::BitOr for BooleanBitfield {
type Output = Self;
fn bitor(self, other: Self) -> Self {
let (biggest, smallest) = if self.len() > other.len() {
(&self, &other)
} else {
(&other, &self)
};
let mut new = biggest.clone();
for i in 0..smallest.len() {
if let Ok(true) = smallest.get(i) {
new.set(i, true);
}
}
new
}
}
impl Encode for BooleanBitfield {
fn is_ssz_fixed_len() -> bool {
false
}
fn ssz_append(&self, buf: &mut Vec<u8>) {
buf.append(&mut self.to_bytes())
}
}
impl Decode for BooleanBitfield {
fn is_ssz_fixed_len() -> bool {
false
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
Ok(BooleanBitfield::from_bytes(bytes))
}
}
// Reverse the bit order of a whole byte vec, so that the ith bit
// of the input vec is placed in the (N - i)th bit of the output vec.
// This function is necessary for converting bitfields to and from YAML,
// as the BitVec library and the hex-parser use opposing bit orders.
fn reverse_bit_order(mut bytes: Vec<u8>) -> Vec<u8> {
bytes.reverse();
bytes.into_iter().map(LookupReverse::swap_bits).collect()
}
impl Serialize for BooleanBitfield {
/// Serde serialization is compliant with the Ethereum YAML test format.
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&encode(self.to_bytes()))
}
}
impl<'de> Deserialize<'de> for BooleanBitfield {
/// Serde serialization is compliant with the Ethereum YAML test format.
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
// We reverse the bit-order so that the BitVec library can read its 0th
// bit from the end of the hex string, e.g.
// "0xef01" => [0xef, 0x01] => [0b1000_0000, 0b1111_1110]
let bytes = deserializer.deserialize_str(PrefixedHexVisitor)?;
Ok(BooleanBitfield::from_bytes(&bytes))
}
}
impl tree_hash::TreeHash for BooleanBitfield {
fn tree_hash_type() -> tree_hash::TreeHashType {
tree_hash::TreeHashType::List
}
fn tree_hash_packed_encoding(&self) -> Vec<u8> {
unreachable!("List should never be packed.")
}
fn tree_hash_packing_factor() -> usize {
unreachable!("List should never be packed.")
}
fn tree_hash_root(&self) -> Vec<u8> {
self.to_bytes().tree_hash_root()
}
}
cached_tree_hash_bytes_as_list!(BooleanBitfield);
#[cfg(test)]
mod tests {
use super::*;
use serde_yaml;
use ssz::ssz_encode;
use tree_hash::TreeHash;
#[test]
pub fn test_cached_tree_hash() {
let original = BooleanBitfield::from_bytes(&vec![18; 12][..]);
let mut cache = cached_tree_hash::TreeHashCache::new(&original).unwrap();
assert_eq!(
cache.tree_hash_root().unwrap().to_vec(),
original.tree_hash_root()
);
let modified = BooleanBitfield::from_bytes(&vec![2; 1][..]);
cache.update(&modified).unwrap();
assert_eq!(
cache.tree_hash_root().unwrap().to_vec(),
modified.tree_hash_root()
);
}
#[test]
fn test_new_bitfield() {
let mut field = BooleanBitfield::new();
let original_len = field.len();
for i in 0..100 {
if i < original_len {
assert!(!field.get(i).unwrap());
} else {
assert!(field.get(i).is_err());
}
let previous = field.set(i, true);
if i < original_len {
assert!(!previous.unwrap());
} else {
assert!(previous.is_none());
}
}
}
#[test]
fn test_empty_bitfield() {
let mut field = BooleanBitfield::from_elem(0, false);
let original_len = field.len();
assert_eq!(original_len, 0);
for i in 0..100 {
if i < original_len {
assert!(!field.get(i).unwrap());
} else {
assert!(field.get(i).is_err());
}
let previous = field.set(i, true);
if i < original_len {
assert!(!previous.unwrap());
} else {
assert!(previous.is_none());
}
}
assert_eq!(field.len(), 100);
assert_eq!(field.num_set_bits(), 100);
}
const INPUT: &[u8] = &[0b0100_0000, 0b0100_0000];
#[test]
fn test_get_from_bitfield() {
let field = BooleanBitfield::from_bytes(INPUT);
let unset = field.get(0).unwrap();
assert!(!unset);
let set = field.get(6).unwrap();
assert!(set);
let set = field.get(14).unwrap();
assert!(set);
}
#[test]
fn test_set_for_bitfield() {
let mut field = BooleanBitfield::from_bytes(INPUT);
let previous = field.set(10, true).unwrap();
assert!(!previous);
let previous = field.get(10).unwrap();
assert!(previous);
let previous = field.set(6, false).unwrap();
assert!(previous);
let previous = field.get(6).unwrap();
assert!(!previous);
}
#[test]
fn test_len() {
let field = BooleanBitfield::from_bytes(INPUT);
assert_eq!(field.len(), 16);
let field = BooleanBitfield::new();
assert_eq!(field.len(), 8);
}
#[test]
fn test_num_set_bits() {
let field = BooleanBitfield::from_bytes(INPUT);
assert_eq!(field.num_set_bits(), 2);
let field = BooleanBitfield::new();
assert_eq!(field.num_set_bits(), 0);
}
#[test]
fn test_to_bytes() {
let field = BooleanBitfield::from_bytes(INPUT);
assert_eq!(field.to_bytes(), INPUT);
let field = BooleanBitfield::new();
assert_eq!(field.to_bytes(), vec![0]);
}
#[test]
fn test_out_of_bounds() {
let mut field = BooleanBitfield::from_bytes(INPUT);
let out_of_bounds_index = field.len();
assert!(field.set(out_of_bounds_index, true).is_none());
assert!(field.len() == out_of_bounds_index + 1);
assert!(field.get(out_of_bounds_index).unwrap());
for i in 0..100 {
if i <= out_of_bounds_index {
assert!(field.set(i, true).is_some());
} else {
assert!(field.set(i, true).is_none());
}
}
}
#[test]
fn test_grows_with_false() {
let input_all_set: &[u8] = &[0b1111_1111, 0b1111_1111];
let mut field = BooleanBitfield::from_bytes(input_all_set);
// Define `a` and `b`, where both are out of bounds and `b` is greater than `a`.
let a = field.len();
let b = a + 1;
// Ensure `a` is out-of-bounds for test integrity.
assert!(field.get(a).is_err());
// Set `b` to `true`. Also, for test integrity, ensure it was previously out-of-bounds.
assert!(field.set(b, true).is_none());
// Ensure that `a` wasn't also set to `true` during the grow.
assert_eq!(field.get(a), Ok(false));
assert_eq!(field.get(b), Ok(true));
}
#[test]
fn test_num_bytes() {
let field = BooleanBitfield::from_bytes(INPUT);
assert_eq!(field.num_bytes(), 2);
let field = BooleanBitfield::from_elem(2, true);
assert_eq!(field.num_bytes(), 1);
let field = BooleanBitfield::from_elem(13, true);
assert_eq!(field.num_bytes(), 2);
}
#[test]
fn test_ssz_encode() {
let field = create_test_bitfield();
assert_eq!(field.as_ssz_bytes(), vec![0b0000_0011, 0b1000_0111]);
let field = BooleanBitfield::from_elem(18, true);
assert_eq!(
field.as_ssz_bytes(),
vec![0b0000_0011, 0b1111_1111, 0b1111_1111]
);
let mut b = BooleanBitfield::new();
b.set(1, true);
assert_eq!(ssz_encode(&b), vec![0b0000_0010]);
}
fn create_test_bitfield() -> BooleanBitfield {
let count = 2 * 8;
let mut field = BooleanBitfield::with_capacity(count);
let indices = &[0, 1, 2, 7, 8, 9];
for &i in indices {
field.set(i, true);
}
field
}
#[test]
fn test_ssz_decode() {
let encoded = vec![0b0000_0011, 0b1000_0111];
let field = BooleanBitfield::from_ssz_bytes(&encoded).unwrap();
let expected = create_test_bitfield();
assert_eq!(field, expected);
let encoded = vec![255, 255, 3];
let field = BooleanBitfield::from_ssz_bytes(&encoded).unwrap();
let expected = BooleanBitfield::from_bytes(&[255, 255, 3]);
assert_eq!(field, expected);
}
#[test]
fn test_serialize_deserialize() {
use serde_yaml::Value;
let data: &[(_, &[_])] = &[
("0x01", &[0b00000001]),
("0xf301", &[0b11110011, 0b00000001]),
];
for (hex_data, bytes) in data {
let bitfield = BooleanBitfield::from_bytes(bytes);
assert_eq!(
serde_yaml::from_str::<BooleanBitfield>(hex_data).unwrap(),
bitfield
);
assert_eq!(
serde_yaml::to_value(&bitfield).unwrap(),
Value::String(hex_data.to_string())
);
}
}
#[test]
fn test_ssz_round_trip() {
let original = BooleanBitfield::from_bytes(&vec![18; 12][..]);
let ssz = ssz_encode(&original);
let decoded = BooleanBitfield::from_ssz_bytes(&ssz).unwrap();
assert_eq!(original, decoded);
}
#[test]
fn test_bitor() {
let a = BooleanBitfield::from_bytes(&vec![2, 8, 1][..]);
let b = BooleanBitfield::from_bytes(&vec![4, 8, 16][..]);
let c = BooleanBitfield::from_bytes(&vec![6, 8, 17][..]);
assert_eq!(c, a | b);
}
#[test]
fn test_is_zero() {
let yes_data: &[&[u8]] = &[&[], &[0], &[0, 0], &[0, 0, 0]];
for bytes in yes_data {
assert!(BooleanBitfield::from_bytes(bytes).is_zero());
}
let no_data: &[&[u8]] = &[&[1], &[6], &[0, 1], &[0, 0, 1], &[0, 0, 255]];
for bytes in no_data {
assert!(!BooleanBitfield::from_bytes(bytes).is_zero());
}
}
#[test]
fn test_intersection() {
let a = BooleanBitfield::from_bytes(&[0b1100, 0b0001]);
let b = BooleanBitfield::from_bytes(&[0b1011, 0b1001]);
let c = BooleanBitfield::from_bytes(&[0b1000, 0b0001]);
assert_eq!(a.intersection(&b), c);
assert_eq!(b.intersection(&a), c);
assert_eq!(a.intersection(&c), c);
assert_eq!(b.intersection(&c), c);
assert_eq!(a.intersection(&a), a);
assert_eq!(b.intersection(&b), b);
assert_eq!(c.intersection(&c), c);
}
#[test]
fn test_union() {
let a = BooleanBitfield::from_bytes(&[0b1100, 0b0001]);
let b = BooleanBitfield::from_bytes(&[0b1011, 0b1001]);
let c = BooleanBitfield::from_bytes(&[0b1111, 0b1001]);
assert_eq!(a.union(&b), c);
assert_eq!(b.union(&a), c);
assert_eq!(a.union(&a), a);
assert_eq!(b.union(&b), b);
assert_eq!(c.union(&c), c);
}
#[test]
fn test_difference() {
let a = BooleanBitfield::from_bytes(&[0b1100, 0b0001]);
let b = BooleanBitfield::from_bytes(&[0b1011, 0b1001]);
let a_b = BooleanBitfield::from_bytes(&[0b0100, 0b0000]);
let b_a = BooleanBitfield::from_bytes(&[0b0011, 0b1000]);
assert_eq!(a.difference(&b), a_b);
assert_eq!(b.difference(&a), b_a);
assert!(a.difference(&a).is_zero());
}
}
@@ -1,21 +0,0 @@
use cached_tree_hash::TreeHashCache;
use ethereum_types::H256 as Hash256;
fn run(vec: &Vec<Hash256>, modified_vec: &Vec<Hash256>) {
let mut cache = TreeHashCache::new(vec).unwrap();
cache.update(modified_vec).unwrap();
}
fn main() {
let n = 2048;
let vec: Vec<Hash256> = (0..n).map(|_| Hash256::random()).collect();
let mut modified_vec = vec.clone();
modified_vec[n - 1] = Hash256::random();
for _ in 0..10_000 {
run(&vec, &modified_vec);
}
}
@@ -1,10 +0,0 @@
use ethereum_types::H256 as Hash256;
use tree_hash::TreeHash;
fn main() {
let n = 2048;
let vec: Vec<Hash256> = (0..n).map(|_| Hash256::random()).collect();
vec.tree_hash_root();
}
-677
View File
@@ -1,677 +0,0 @@
use cached_tree_hash::{merkleize::merkleize, *};
use ethereum_types::H256 as Hash256;
use int_to_bytes::int_to_bytes32;
use tree_hash_derive::{CachedTreeHash, TreeHash};
#[test]
fn modifications() {
let n = 2048;
let vec: Vec<Hash256> = (0..n).map(|_| Hash256::random()).collect();
let mut cache = TreeHashCache::new(&vec).unwrap();
cache.update(&vec).unwrap();
let modifications = cache.chunk_modified.iter().filter(|b| **b).count();
assert_eq!(modifications, 0);
let mut modified_vec = vec.clone();
modified_vec[n - 1] = Hash256::random();
cache.update(&modified_vec).unwrap();
let modifications = cache.chunk_modified.iter().filter(|b| **b).count();
assert_eq!(modifications, n.trailing_zeros() as usize + 2);
}
#[derive(Clone, Debug, TreeHash, CachedTreeHash)]
pub struct NestedStruct {
pub a: u64,
pub b: Inner,
}
fn test_routine<T>(original: T, modified: Vec<T>)
where
T: CachedTreeHash + std::fmt::Debug,
{
let mut cache = TreeHashCache::new(&original).unwrap();
let standard_root = original.tree_hash_root();
let cached_root = cache.tree_hash_root().unwrap();
assert_eq!(standard_root, cached_root, "Initial cache build failed.");
for (i, modified) in modified.iter().enumerate() {
println!("-- Start of modification {} --", i);
// Update the existing hasher.
cache
.update(modified)
.expect(&format!("Modification {}", i));
// Create a new hasher from the "modified" struct.
let modified_cache = TreeHashCache::new(modified).unwrap();
assert_eq!(
cache.chunk_modified.len(),
modified_cache.chunk_modified.len(),
"Number of chunks is different"
);
assert_eq!(
cache.bytes.len(),
modified_cache.bytes.len(),
"Number of bytes is different"
);
assert_eq!(cache.bytes, modified_cache.bytes, "Bytes are different");
assert_eq!(
cache.schemas.len(),
modified_cache.schemas.len(),
"Number of schemas is different"
);
assert_eq!(
cache.schemas, modified_cache.schemas,
"Schemas are different"
);
// Test the root generated by the updated hasher matches a non-cached tree hash root.
let standard_root = modified.tree_hash_root();
let cached_root = cache
.tree_hash_root()
.expect(&format!("Modification {}", i));
assert_eq!(
standard_root, cached_root,
"Modification {} failed. \n Cache: {:?}",
i, cache
);
}
}
#[test]
fn test_nested_struct() {
let original = NestedStruct {
a: 42,
b: Inner {
a: 12,
b: 13,
c: 14,
d: 15,
},
};
let modified = vec![NestedStruct {
a: 99,
..original.clone()
}];
test_routine(original, modified);
}
#[test]
fn test_inner() {
let original = Inner {
a: 12,
b: 13,
c: 14,
d: 15,
};
let modified = vec![Inner {
a: 99,
..original.clone()
}];
test_routine(original, modified);
}
#[test]
fn test_vec_of_hash256() {
let n = 16;
let original: Vec<Hash256> = (0..n).map(|_| Hash256::random()).collect();
let modified: Vec<Vec<Hash256>> = vec![
original[..].to_vec(),
original[0..n / 2].to_vec(),
vec![],
original[0..1].to_vec(),
original[0..3].to_vec(),
original[0..n - 12].to_vec(),
];
test_routine(original, modified);
}
#[test]
fn test_vec_of_u64() {
let original: Vec<u64> = vec![1, 2, 3, 4, 5];
let modified: Vec<Vec<u64>> = vec![
vec![1, 2, 3, 4, 42],
vec![1, 2, 3, 4],
vec![],
vec![42; 2_usize.pow(4)],
vec![],
vec![],
vec![1, 2, 3, 4, 42],
vec![1, 2, 3],
vec![1],
];
test_routine(original, modified);
}
#[test]
fn test_nested_list_of_u64() {
let original: Vec<Vec<u64>> = vec![vec![42]];
let modified = vec![
vec![vec![1]],
vec![vec![1], vec![2]],
vec![vec![1], vec![3], vec![4]],
vec![],
vec![vec![1], vec![3], vec![4]],
vec![],
vec![vec![1, 2], vec![3], vec![4, 5, 6, 7, 8]],
vec![],
vec![vec![1], vec![2], vec![3]],
vec![vec![1, 2, 3, 4, 5, 6], vec![1, 2, 3, 4, 5, 6, 7]],
vec![vec![], vec![], vec![]],
vec![vec![0, 0, 0], vec![0], vec![0]],
];
test_routine(original, modified);
}
#[test]
fn test_shrinking_vec_of_vec() {
let original: Vec<Vec<u64>> = vec![vec![1], vec![2], vec![3], vec![4], vec![5]];
let modified: Vec<Vec<u64>> = original[0..3].to_vec();
let new_cache = TreeHashCache::new(&modified).unwrap();
let mut modified_cache = TreeHashCache::new(&original).unwrap();
modified_cache.update(&modified).unwrap();
assert_eq!(
new_cache.schemas.len(),
modified_cache.schemas.len(),
"Schema count is different"
);
assert_eq!(
new_cache.chunk_modified.len(),
modified_cache.chunk_modified.len(),
"Chunk count is different"
);
}
#[derive(Clone, Debug, TreeHash, CachedTreeHash)]
pub struct StructWithVec {
pub a: u64,
pub b: Inner,
pub c: Vec<u64>,
}
#[test]
fn test_struct_with_vec() {
let original = StructWithVec {
a: 42,
b: Inner {
a: 12,
b: 13,
c: 14,
d: 15,
},
c: vec![1, 2, 3, 4, 5],
};
let modified = vec![
StructWithVec {
a: 99,
..original.clone()
},
StructWithVec {
a: 100,
..original.clone()
},
StructWithVec {
c: vec![1, 2, 3, 4, 5],
..original.clone()
},
StructWithVec {
c: vec![1, 3, 4, 5, 6],
..original.clone()
},
StructWithVec {
c: vec![1, 3, 4, 5, 6, 7, 8, 9],
..original.clone()
},
StructWithVec {
c: vec![1, 3, 4, 5],
..original.clone()
},
StructWithVec {
b: Inner {
a: u64::max_value(),
b: u64::max_value(),
c: u64::max_value(),
d: u64::max_value(),
},
c: vec![],
..original.clone()
},
StructWithVec {
b: Inner {
a: 0,
b: 1,
c: 2,
d: 3,
},
..original.clone()
},
];
test_routine(original, modified);
}
#[test]
fn test_vec_of_struct_with_vec() {
let a = StructWithVec {
a: 42,
b: Inner {
a: 12,
b: 13,
c: 14,
d: 15,
},
c: vec![1, 2, 3, 4, 5],
};
let b = StructWithVec {
c: vec![],
..a.clone()
};
let c = StructWithVec {
b: Inner {
a: 99,
b: 100,
c: 101,
d: 102,
},
..a.clone()
};
let d = StructWithVec { a: 0, ..a.clone() };
let original: Vec<StructWithVec> = vec![a.clone(), c.clone()];
let modified = vec![
vec![a.clone(), c.clone()],
vec![],
vec![a.clone(), b.clone(), c.clone(), d.clone()],
vec![b.clone(), a.clone(), c.clone(), d.clone()],
vec![],
vec![a.clone()],
vec![],
vec![a.clone(), b.clone(), c.clone(), d.clone()],
];
test_routine(original, modified);
}
#[derive(Clone, Debug, TreeHash, CachedTreeHash)]
pub struct StructWithVecOfStructs {
pub a: u64,
pub b: Inner,
pub c: Vec<Inner>,
}
fn get_inners() -> Vec<Inner> {
vec![
Inner {
a: 12,
b: 13,
c: 14,
d: 15,
},
Inner {
a: 99,
b: 100,
c: 101,
d: 102,
},
Inner {
a: 255,
b: 256,
c: 257,
d: 0,
},
Inner {
a: 1000,
b: 2000,
c: 3000,
d: 0,
},
Inner {
a: 0,
b: 0,
c: 0,
d: 0,
},
]
}
fn get_struct_with_vec_of_structs() -> Vec<StructWithVecOfStructs> {
let inner_a = Inner {
a: 12,
b: 13,
c: 14,
d: 15,
};
let inner_b = Inner {
a: 99,
b: 100,
c: 101,
d: 102,
};
let inner_c = Inner {
a: 255,
b: 256,
c: 257,
d: 0,
};
let a = StructWithVecOfStructs {
a: 42,
b: inner_a.clone(),
c: vec![inner_a.clone(), inner_b.clone(), inner_c.clone()],
};
let b = StructWithVecOfStructs {
c: vec![],
..a.clone()
};
let c = StructWithVecOfStructs {
a: 800,
..a.clone()
};
let d = StructWithVecOfStructs {
b: inner_c.clone(),
..a.clone()
};
let e = StructWithVecOfStructs {
c: vec![inner_a.clone(), inner_b.clone()],
..a.clone()
};
let f = StructWithVecOfStructs {
c: vec![inner_a.clone()],
..a.clone()
};
vec![a, b, c, d, e, f]
}
#[test]
fn test_struct_with_vec_of_structs() {
let variants = get_struct_with_vec_of_structs();
test_routine(variants[0].clone(), variants.clone());
test_routine(variants[1].clone(), variants.clone());
test_routine(variants[2].clone(), variants.clone());
test_routine(variants[3].clone(), variants.clone());
test_routine(variants[4].clone(), variants.clone());
test_routine(variants[5].clone(), variants.clone());
}
#[derive(Clone, Debug, TreeHash, CachedTreeHash)]
pub struct StructWithVecOfStructWithVecOfStructs {
pub a: Vec<StructWithVecOfStructs>,
pub b: u64,
}
#[test]
fn test_struct_with_vec_of_struct_with_vec_of_structs() {
let structs = get_struct_with_vec_of_structs();
let variants = vec![
StructWithVecOfStructWithVecOfStructs {
a: structs[..].to_vec(),
b: 99,
},
StructWithVecOfStructWithVecOfStructs { a: vec![], b: 99 },
StructWithVecOfStructWithVecOfStructs {
a: structs[0..2].to_vec(),
b: 99,
},
StructWithVecOfStructWithVecOfStructs {
a: structs[0..2].to_vec(),
b: 100,
},
StructWithVecOfStructWithVecOfStructs {
a: structs[0..1].to_vec(),
b: 100,
},
StructWithVecOfStructWithVecOfStructs {
a: structs[0..4].to_vec(),
b: 100,
},
StructWithVecOfStructWithVecOfStructs {
a: structs[0..5].to_vec(),
b: 8,
},
];
for v in &variants {
test_routine(v.clone(), variants.clone());
}
}
#[derive(Clone, Debug, TreeHash, CachedTreeHash)]
pub struct StructWithTwoVecs {
pub a: Vec<Inner>,
pub b: Vec<Inner>,
}
fn get_struct_with_two_vecs() -> Vec<StructWithTwoVecs> {
let inners = get_inners();
vec![
StructWithTwoVecs {
a: inners[..].to_vec(),
b: inners[..].to_vec(),
},
StructWithTwoVecs {
a: inners[0..1].to_vec(),
b: inners[..].to_vec(),
},
StructWithTwoVecs {
a: inners[0..1].to_vec(),
b: inners[0..2].to_vec(),
},
StructWithTwoVecs {
a: inners[0..4].to_vec(),
b: inners[0..2].to_vec(),
},
StructWithTwoVecs {
a: vec![],
b: inners[..].to_vec(),
},
StructWithTwoVecs {
a: inners[..].to_vec(),
b: vec![],
},
StructWithTwoVecs {
a: inners[0..3].to_vec(),
b: inners[0..1].to_vec(),
},
]
}
#[test]
fn test_struct_with_two_vecs() {
let variants = get_struct_with_two_vecs();
for v in &variants {
test_routine(v.clone(), variants.clone());
}
}
#[test]
fn test_vec_of_struct_with_two_vecs() {
let structs = get_struct_with_two_vecs();
let variants = vec![
structs[0..].to_vec(),
structs[0..2].to_vec(),
structs[2..3].to_vec(),
vec![],
structs[2..4].to_vec(),
];
test_routine(variants[0].clone(), vec![variants[2].clone()]);
for v in &variants {
test_routine(v.clone(), variants.clone());
}
}
#[derive(Clone, Debug, TreeHash, CachedTreeHash)]
pub struct U64AndTwoStructs {
pub a: u64,
pub b: Inner,
pub c: Inner,
}
#[test]
fn test_u64_and_two_structs() {
let inners = get_inners();
let variants = vec![
U64AndTwoStructs {
a: 99,
b: inners[0].clone(),
c: inners[1].clone(),
},
U64AndTwoStructs {
a: 10,
b: inners[2].clone(),
c: inners[3].clone(),
},
U64AndTwoStructs {
a: 0,
b: inners[1].clone(),
c: inners[1].clone(),
},
U64AndTwoStructs {
a: 0,
b: inners[1].clone(),
c: inners[1].clone(),
},
];
for v in &variants {
test_routine(v.clone(), variants.clone());
}
}
#[derive(Clone, Debug, TreeHash, CachedTreeHash)]
pub struct Inner {
pub a: u64,
pub b: u64,
pub c: u64,
pub d: u64,
}
fn generic_test(index: usize) {
let inner = Inner {
a: 1,
b: 2,
c: 3,
d: 4,
};
let mut cache = TreeHashCache::new(&inner).unwrap();
let changed_inner = match index {
0 => Inner {
a: 42,
..inner.clone()
},
1 => Inner {
b: 42,
..inner.clone()
},
2 => Inner {
c: 42,
..inner.clone()
},
3 => Inner {
d: 42,
..inner.clone()
},
_ => panic!("bad index"),
};
changed_inner.update_tree_hash_cache(&mut cache).unwrap();
let data1 = int_to_bytes32(1);
let data2 = int_to_bytes32(2);
let data3 = int_to_bytes32(3);
let data4 = int_to_bytes32(4);
let mut data = vec![data1, data2, data3, data4];
data[index] = int_to_bytes32(42);
let expected = merkleize(join(data));
let (cache_bytes, _, _) = cache.into_components();
assert_eq!(expected, cache_bytes);
}
#[test]
fn cached_hash_on_inner() {
generic_test(0);
generic_test(1);
generic_test(2);
generic_test(3);
}
#[test]
fn inner_builds() {
let data1 = int_to_bytes32(1);
let data2 = int_to_bytes32(2);
let data3 = int_to_bytes32(3);
let data4 = int_to_bytes32(4);
let data = join(vec![data1, data2, data3, data4]);
let expected = merkleize(data);
let inner = Inner {
a: 1,
b: 2,
c: 3,
d: 4,
};
let (cache_bytes, _, _) = TreeHashCache::new(&inner).unwrap().into_components();
assert_eq!(expected, cache_bytes);
}
fn join(many: Vec<Vec<u8>>) -> Vec<u8> {
let mut all = vec![];
for one in many {
all.extend_from_slice(&mut one.clone())
}
all
}
+1 -1
View File
@@ -46,7 +46,7 @@ impl Eth2Config {
/// invalid.
pub fn apply_cli_args(&mut self, args: &ArgMatches) -> Result<(), &'static str> {
if args.is_present("recent-genesis") {
self.spec.genesis_time = recent_genesis_time()
self.spec.min_genesis_time = recent_genesis_time()
}
Ok(())
-13
View File
@@ -1,13 +0,0 @@
[package]
name = "fixed_len_vec"
version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2018"
[dependencies]
cached_tree_hash = { path = "../cached_tree_hash" }
tree_hash = { path = "../tree_hash" }
serde = "1.0"
serde_derive = "1.0"
eth2_ssz = { path = "../ssz" }
typenum = "1.10"
-140
View File
@@ -1,140 +0,0 @@
use super::*;
impl<T, N: Unsigned> tree_hash::TreeHash for FixedLenVec<T, N>
where
T: tree_hash::TreeHash,
{
fn tree_hash_type() -> tree_hash::TreeHashType {
tree_hash::TreeHashType::Vector
}
fn tree_hash_packed_encoding(&self) -> Vec<u8> {
unreachable!("Vector should never be packed.")
}
fn tree_hash_packing_factor() -> usize {
unreachable!("Vector should never be packed.")
}
fn tree_hash_root(&self) -> Vec<u8> {
tree_hash::impls::vec_tree_hash_root(&self.vec)
}
}
impl<T, N: Unsigned> cached_tree_hash::CachedTreeHash for FixedLenVec<T, N>
where
T: cached_tree_hash::CachedTreeHash + tree_hash::TreeHash,
{
fn new_tree_hash_cache(
&self,
depth: usize,
) -> Result<cached_tree_hash::TreeHashCache, cached_tree_hash::Error> {
let (cache, _overlay) = cached_tree_hash::vec::new_tree_hash_cache(&self.vec, depth)?;
Ok(cache)
}
fn tree_hash_cache_schema(&self, depth: usize) -> cached_tree_hash::BTreeSchema {
cached_tree_hash::vec::produce_schema(&self.vec, depth)
}
fn update_tree_hash_cache(
&self,
cache: &mut cached_tree_hash::TreeHashCache,
) -> Result<(), cached_tree_hash::Error> {
cached_tree_hash::vec::update_tree_hash_cache(&self.vec, cache)?;
Ok(())
}
}
impl<T, N: Unsigned> ssz::Encode for FixedLenVec<T, N>
where
T: ssz::Encode,
{
fn is_ssz_fixed_len() -> bool {
true
}
fn ssz_fixed_len() -> usize {
if <Self as ssz::Encode>::is_ssz_fixed_len() {
T::ssz_fixed_len() * N::to_usize()
} else {
ssz::BYTES_PER_LENGTH_OFFSET
}
}
fn ssz_append(&self, buf: &mut Vec<u8>) {
if T::is_ssz_fixed_len() {
buf.reserve(T::ssz_fixed_len() * self.len());
for item in &self.vec {
item.ssz_append(buf);
}
} else {
let mut encoder = ssz::SszEncoder::list(buf, self.len() * ssz::BYTES_PER_LENGTH_OFFSET);
for item in &self.vec {
encoder.append(item);
}
encoder.finalize();
}
}
}
impl<T, N: Unsigned> ssz::Decode for FixedLenVec<T, N>
where
T: ssz::Decode + Default,
{
fn is_ssz_fixed_len() -> bool {
T::is_ssz_fixed_len()
}
fn ssz_fixed_len() -> usize {
if <Self as ssz::Decode>::is_ssz_fixed_len() {
T::ssz_fixed_len() * N::to_usize()
} else {
ssz::BYTES_PER_LENGTH_OFFSET
}
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
if bytes.is_empty() {
Ok(FixedLenVec::from(vec![]))
} else if T::is_ssz_fixed_len() {
bytes
.chunks(T::ssz_fixed_len())
.map(|chunk| T::from_ssz_bytes(chunk))
.collect::<Result<Vec<T>, _>>()
.and_then(|vec| Ok(vec.into()))
} else {
ssz::decode_list_of_variable_length_items(bytes).and_then(|vec| Ok(vec.into()))
}
}
}
#[cfg(test)]
mod ssz_tests {
use super::*;
use ssz::*;
use typenum::*;
#[test]
fn encode() {
let vec: FixedLenVec<u16, U2> = vec![0; 2].into();
assert_eq!(vec.as_ssz_bytes(), vec![0, 0, 0, 0]);
assert_eq!(<FixedLenVec<u16, U2> as Encode>::ssz_fixed_len(), 4);
}
fn round_trip<T: Encode + Decode + std::fmt::Debug + PartialEq>(item: T) {
let encoded = &item.as_ssz_bytes();
assert_eq!(T::from_ssz_bytes(&encoded), Ok(item));
}
#[test]
fn u16_len_8() {
round_trip::<FixedLenVec<u16, U8>>(vec![42; 8].into());
round_trip::<FixedLenVec<u16, U8>>(vec![0; 8].into());
}
}
-134
View File
@@ -1,134 +0,0 @@
use serde_derive::{Deserialize, Serialize};
use std::marker::PhantomData;
use std::ops::{Deref, Index, IndexMut};
use std::slice::SliceIndex;
use typenum::Unsigned;
pub use typenum;
mod impls;
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct FixedLenVec<T, N> {
vec: Vec<T>,
_phantom: PhantomData<N>,
}
impl<T, N: Unsigned> FixedLenVec<T, N> {
pub fn len(&self) -> usize {
self.vec.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn capacity() -> usize {
N::to_usize()
}
}
impl<T: Default, N: Unsigned> From<Vec<T>> for FixedLenVec<T, N> {
fn from(mut vec: Vec<T>) -> Self {
vec.resize_with(Self::capacity(), Default::default);
Self {
vec,
_phantom: PhantomData,
}
}
}
impl<T, N: Unsigned> Into<Vec<T>> for FixedLenVec<T, N> {
fn into(self) -> Vec<T> {
self.vec
}
}
impl<T, N: Unsigned> Default for FixedLenVec<T, N> {
fn default() -> Self {
Self {
vec: Vec::default(),
_phantom: PhantomData,
}
}
}
impl<T, N: Unsigned, I: SliceIndex<[T]>> Index<I> for FixedLenVec<T, N> {
type Output = I::Output;
#[inline]
fn index(&self, index: I) -> &Self::Output {
Index::index(&self.vec, index)
}
}
impl<T, N: Unsigned, I: SliceIndex<[T]>> IndexMut<I> for FixedLenVec<T, N> {
#[inline]
fn index_mut(&mut self, index: I) -> &mut Self::Output {
IndexMut::index_mut(&mut self.vec, index)
}
}
impl<T, N: Unsigned> Deref for FixedLenVec<T, N> {
type Target = [T];
fn deref(&self) -> &[T] {
&self.vec[..]
}
}
#[cfg(test)]
mod test {
use super::*;
use typenum::*;
#[test]
fn indexing() {
let vec = vec![1, 2];
let mut fixed: FixedLenVec<u64, U8192> = vec.clone().into();
assert_eq!(fixed[0], 1);
assert_eq!(&fixed[0..1], &vec[0..1]);
assert_eq!((&fixed[..]).len(), 8192);
fixed[1] = 3;
assert_eq!(fixed[1], 3);
}
#[test]
fn length() {
let vec = vec![42; 5];
let fixed: FixedLenVec<u64, U4> = FixedLenVec::from(vec.clone());
assert_eq!(&fixed[..], &vec[0..4]);
let vec = vec![42; 3];
let fixed: FixedLenVec<u64, U4> = FixedLenVec::from(vec.clone());
assert_eq!(&fixed[0..3], &vec[..]);
assert_eq!(&fixed[..], &vec![42, 42, 42, 0][..]);
let vec = vec![];
let fixed: FixedLenVec<u64, U4> = FixedLenVec::from(vec.clone());
assert_eq!(&fixed[..], &vec![0, 0, 0, 0][..]);
}
#[test]
fn deref() {
let vec = vec![0, 2, 4, 6];
let fixed: FixedLenVec<u64, U4> = FixedLenVec::from(vec);
assert_eq!(fixed.get(0), Some(&0));
assert_eq!(fixed.get(3), Some(&6));
assert_eq!(fixed.get(4), None);
}
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}
-7
View File
@@ -1,7 +0,0 @@
[package]
name = "honey-badger-split"
version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2018"
[dependencies]
-117
View File
@@ -1,117 +0,0 @@
/// A function for splitting a list into N pieces.
///
/// We have titled it the "honey badger split" because of its robustness. It don't care.
/// Iterator for the honey_badger_split function
pub struct Split<'a, T: 'a> {
n: usize,
current_pos: usize,
list: &'a [T],
list_length: usize,
}
impl<'a, T> Iterator for Split<'a, T> {
type Item = &'a [T];
fn next(&mut self) -> Option<Self::Item> {
self.current_pos += 1;
if self.current_pos <= self.n {
match self.list.get(
self.list_length * (self.current_pos - 1) / self.n
..self.list_length * self.current_pos / self.n,
) {
Some(v) => Some(v),
None => unreachable!(),
}
} else {
None
}
}
}
/// Splits a slice into chunks of size n. All positive n values are applicable,
/// hence the honey_badger prefix.
///
/// Returns an iterator over the original list.
pub trait SplitExt<T> {
fn honey_badger_split(&self, n: usize) -> Split<T>;
}
impl<T> SplitExt<T> for [T] {
fn honey_badger_split(&self, n: usize) -> Split<T> {
Split {
n,
current_pos: 0,
list: &self,
list_length: self.len(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn alternative_split_at_index<T>(indices: &[T], index: usize, count: usize) -> &[T] {
let start = (indices.len() * index) / count;
let end = (indices.len() * (index + 1)) / count;
&indices[start..end]
}
fn alternative_split<T: Clone>(input: &[T], n: usize) -> Vec<&[T]> {
(0..n)
.into_iter()
.map(|i| alternative_split_at_index(&input, i, n))
.collect()
}
fn honey_badger_vs_alternative_fn(num_items: usize, num_chunks: usize) {
let input: Vec<usize> = (0..num_items).collect();
let hb: Vec<&[usize]> = input.honey_badger_split(num_chunks).collect();
let spec: Vec<&[usize]> = alternative_split(&input, num_chunks);
assert_eq!(hb, spec);
}
#[test]
fn vs_eth_spec_fn() {
for i in 0..10 {
for j in 0..10 {
honey_badger_vs_alternative_fn(i, j);
}
}
}
#[test]
fn test_honey_badger_split() {
/*
* These test cases are generated from the eth2.0 spec `split()`
* function at commit cbd254a.
*/
let input: Vec<usize> = vec![0, 1, 2, 3];
let output: Vec<&[usize]> = input.honey_badger_split(2).collect();
assert_eq!(output, vec![&[0, 1], &[2, 3]]);
let input: Vec<usize> = vec![0, 1, 2, 3];
let output: Vec<&[usize]> = input.honey_badger_split(6).collect();
let expected: Vec<&[usize]> = vec![&[], &[0], &[1], &[], &[2], &[3]];
assert_eq!(output, expected);
let input: Vec<usize> = vec![0, 1, 2, 3];
let output: Vec<&[usize]> = input.honey_badger_split(10).collect();
let expected: Vec<&[usize]> = vec![&[], &[], &[0], &[], &[1], &[], &[], &[2], &[], &[3]];
assert_eq!(output, expected);
let input: Vec<usize> = vec![0];
let output: Vec<&[usize]> = input.honey_badger_split(5).collect();
let expected: Vec<&[usize]> = vec![&[], &[], &[], &[], &[0]];
assert_eq!(output, expected);
let input: Vec<usize> = vec![0, 1, 2];
let output: Vec<&[usize]> = input.honey_badger_split(2).collect();
let expected: Vec<&[usize]> = vec![&[0], &[1, 2]];
assert_eq!(output, expected);
}
}
+1 -6
View File
@@ -1,6 +1,6 @@
[package]
name = "eth2_ssz"
version = "0.1.0"
version = "0.1.2"
authors = ["Paul Hauner <paul@sigmaprime.io>"]
edition = "2018"
description = "SimpleSerialize (SSZ) as used in Ethereum 2.0"
@@ -9,12 +9,7 @@ license = "Apache-2.0"
[lib]
name = "ssz"
[[bench]]
name = "benches"
harness = false
[dev-dependencies]
criterion = "0.2"
eth2_ssz_derive = "0.1.0"
[dependencies]
-80
View File
@@ -1,80 +0,0 @@
#[macro_use]
extern crate criterion;
use criterion::black_box;
use criterion::{Benchmark, Criterion};
use ssz::{Decode, Encode};
use ssz_derive::{Decode, Encode};
#[derive(Clone, Copy, Encode, Decode)]
pub struct FixedLen {
a: u64,
b: u64,
c: u64,
d: u64,
}
fn criterion_benchmark(c: &mut Criterion) {
let n = 8196;
let vec: Vec<u64> = vec![4242; 8196];
c.bench(
&format!("vec_of_{}_u64", n),
Benchmark::new("as_ssz_bytes", move |b| {
b.iter_with_setup(|| vec.clone(), |vec| black_box(vec.as_ssz_bytes()))
})
.sample_size(100),
);
let vec: Vec<u64> = vec![4242; 8196];
let bytes = vec.as_ssz_bytes();
c.bench(
&format!("vec_of_{}_u64", n),
Benchmark::new("from_ssz_bytes", move |b| {
b.iter_with_setup(
|| bytes.clone(),
|bytes| {
let vec: Vec<u64> = Vec::from_ssz_bytes(&bytes).unwrap();
black_box(vec)
},
)
})
.sample_size(100),
);
let fixed_len = FixedLen {
a: 42,
b: 42,
c: 42,
d: 42,
};
let fixed_len_vec: Vec<FixedLen> = vec![fixed_len; 8196];
let vec = fixed_len_vec.clone();
c.bench(
&format!("vec_of_{}_struct", n),
Benchmark::new("as_ssz_bytes", move |b| {
b.iter_with_setup(|| vec.clone(), |vec| black_box(vec.as_ssz_bytes()))
})
.sample_size(100),
);
let vec = fixed_len_vec.clone();
let bytes = vec.as_ssz_bytes();
c.bench(
&format!("vec_of_{}_struct", n),
Benchmark::new("from_ssz_bytes", move |b| {
b.iter_with_setup(
|| bytes.clone(),
|bytes| {
let vec: Vec<u64> = Vec::from_ssz_bytes(&bytes).unwrap();
black_box(vec)
},
)
})
.sample_size(100),
);
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
+3 -3
View File
@@ -1,9 +1,9 @@
//! Provides encoding (serialization) and decoding (deserialization) in the SimpleSerialize (SSZ)
//! format designed for use in Ethereum 2.0.
//!
//! Conforms to
//! [v0.7.1](https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/simple-serialize.md) of the
//! Ethereum 2.0 specification.
//! Adheres to the Ethereum 2.0 [SSZ
//! specification](https://github.com/ethereum/eth2.0-specs/blob/v0.8.1/specs/simple-serialize.md)
//! at v0.8.1 .
//!
//! ## Example
//!
+5 -1
View File
@@ -1,9 +1,12 @@
[package]
name = "ssz_types"
name = "eth2_ssz_types"
version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2018"
[lib]
name = "ssz_types"
[dependencies]
cached_tree_hash = { path = "../cached_tree_hash" }
tree_hash = { path = "../tree_hash" }
@@ -15,3 +18,4 @@ typenum = "1.10"
[dev-dependencies]
serde_yaml = "0.8"
tree_hash_derive = { path = "../tree_hash_derive" }
+244 -209
View File
@@ -1,3 +1,4 @@
use crate::tree_hash::bitfield_bytes_tree_hash_root;
use crate::Error;
use core::marker::PhantomData;
use serde::de::{Deserialize, Deserializer};
@@ -82,9 +83,9 @@ pub type BitVector<N> = Bitfield<Fixed<N>>;
///
/// ## Note
///
/// The internal representation of the bitfield is the same as that required by SSZ. The highest
/// The internal representation of the bitfield is the same as that required by SSZ. The lowest
/// byte (by `Vec` index) stores the lowest bit-indices and the right-most bit stores the lowest
/// bit-index. E.g., `vec![0b0000_0010, 0b0000_0001]` has bits `0, 9` set.
/// bit-index. E.g., `vec![0b0000_0001, 0b0000_0010]` has bits `0, 9` set.
#[derive(Clone, Debug, PartialEq)]
pub struct Bitfield<T> {
bytes: Vec<u8>,
@@ -136,15 +137,21 @@ impl<N: Unsigned + Clone> Bitfield<Variable<N>> {
/// ```
pub fn into_bytes(self) -> Vec<u8> {
let len = self.len();
let mut bytes = self.as_slice().to_vec();
let mut bytes = self.bytes;
while bytes_for_bit_len(len + 1) > bytes.len() {
bytes.insert(0, 0);
}
bytes.resize(bytes_for_bit_len(len + 1), 0);
let mut bitfield: Bitfield<Variable<N>> = Bitfield::from_raw_bytes(bytes, len + 1)
.expect("Bitfield capacity has been confirmed earlier.");
bitfield.set(len, true).expect("Bitfield index must exist.");
.unwrap_or_else(|_| {
unreachable!(
"Bitfield with {} bytes must have enough capacity for {} bits.",
bytes_for_bit_len(len + 1),
len + 1
)
});
bitfield
.set(len, true)
.expect("len must be in bounds for bitfield.");
bitfield.bytes
}
@@ -171,9 +178,7 @@ impl<N: Unsigned + Clone> Bitfield<Variable<N>> {
let mut bytes = initial_bitfield.into_raw_bytes();
if bytes_for_bit_len(len) < bytes.len() && bytes != [0] {
bytes.remove(0);
}
bytes.truncate(bytes_for_bit_len(len));
Self::from_raw_bytes(bytes, len)
} else {
@@ -183,6 +188,34 @@ impl<N: Unsigned + Clone> Bitfield<Variable<N>> {
})
}
}
/// Compute the intersection of two BitLists of potentially different lengths.
///
/// Return a new BitList with length equal to the shorter of the two inputs.
pub fn intersection(&self, other: &Self) -> Self {
let min_len = std::cmp::min(self.len(), other.len());
let mut result = Self::with_capacity(min_len).expect("min len always less than N");
// Bitwise-and the bytes together, starting from the left of each vector. This takes care
// of masking out any entries beyond `min_len` as well, assuming the bitfield doesn't
// contain any set bits beyond its length.
for i in 0..result.bytes.len() {
result.bytes[i] = self.bytes[i] & other.bytes[i];
}
result
}
/// Compute the union of two BitLists of potentially different lengths.
///
/// Return a new BitList with length equal to the longer of the two inputs.
pub fn union(&self, other: &Self) -> Self {
let max_len = std::cmp::max(self.len(), other.len());
let mut result = Self::with_capacity(max_len).expect("max len always less than N");
for i in 0..result.bytes.len() {
result.bytes[i] =
self.bytes.get(i).copied().unwrap_or(0) | other.bytes.get(i).copied().unwrap_or(0);
}
result
}
}
impl<N: Unsigned + Clone> Bitfield<Fixed<N>> {
@@ -238,14 +271,13 @@ impl<T: BitfieldBehaviour> Bitfield<T> {
///
/// Returns `None` if `i` is out-of-bounds of `self`.
pub fn set(&mut self, i: usize, value: bool) -> Result<(), Error> {
if i < self.len {
let byte = {
let num_bytes = self.bytes.len();
let offset = i / 8;
self.bytes
.get_mut(num_bytes - offset - 1)
.expect("Cannot be OOB if less than self.len")
};
let len = self.len;
if i < len {
let byte = self
.bytes
.get_mut(i / 8)
.ok_or_else(|| Error::OutOfBounds { i, len })?;
if value {
*byte |= 1 << (i % 8)
@@ -264,13 +296,10 @@ impl<T: BitfieldBehaviour> Bitfield<T> {
/// Returns `None` if `i` is out-of-bounds of `self`.
pub fn get(&self, i: usize) -> Result<bool, Error> {
if i < self.len {
let byte = {
let num_bytes = self.bytes.len();
let offset = i / 8;
self.bytes
.get(num_bytes - offset - 1)
.expect("Cannot be OOB if less than self.len")
};
let byte = self
.bytes
.get(i / 8)
.ok_or_else(|| Error::OutOfBounds { i, len: self.len })?;
Ok(*byte & 1 << (i % 8) > 0)
} else {
@@ -328,7 +357,7 @@ impl<T: BitfieldBehaviour> Bitfield<T> {
// Ensure there are no bits higher than `bit_len` that are set to true.
let (mask, _) = u8::max_value().overflowing_shr(8 - (bit_len as u32 % 8));
if (bytes.first().expect("Guarded against empty bytes") & !mask) == 0 {
if (bytes.last().expect("Guarded against empty bytes") & !mask) == 0 {
Ok(Self {
bytes,
len: bit_len,
@@ -343,10 +372,12 @@ impl<T: BitfieldBehaviour> Bitfield<T> {
/// Returns the `Some(i)` where `i` is the highest index with a set bit. Returns `None` if
/// there are no set bits.
pub fn highest_set_bit(&self) -> Option<usize> {
let byte_i = self.bytes.iter().position(|byte| *byte > 0)?;
let bit_i = 7 - self.bytes[byte_i].leading_zeros() as usize;
Some((self.bytes.len().saturating_sub(1) - byte_i) * 8 + bit_i)
self.bytes
.iter()
.enumerate()
.rev()
.find(|(_, byte)| **byte > 0)
.map(|(i, byte)| i * 8 + 7 - byte.leading_zeros() as usize)
}
/// Returns an iterator across bitfield `bool` values, starting at the lowest index.
@@ -362,86 +393,51 @@ impl<T: BitfieldBehaviour> Bitfield<T> {
self.bytes.iter().all(|byte| *byte == 0)
}
/// Compute the intersection (binary-and) of this bitfield with another.
/// Returns the number of bits that are set to `true`.
pub fn num_set_bits(&self) -> usize {
self.bytes
.iter()
.map(|byte| byte.count_ones() as usize)
.sum()
}
/// Compute the difference of this Bitfield and another of potentially different length.
pub fn difference(&self, other: &Self) -> Self {
let mut result = self.clone();
result.difference_inplace(other);
result
}
/// Compute the difference of this Bitfield and another of potentially different length.
pub fn difference_inplace(&mut self, other: &Self) {
let min_byte_len = std::cmp::min(self.bytes.len(), other.bytes.len());
for i in 0..min_byte_len {
self.bytes[i] &= !other.bytes[i];
}
}
/// Shift the bits to higher indices, filling the lower indices with zeroes.
///
/// Returns `None` if `self.is_comparable(other) == false`.
pub fn intersection(&self, other: &Self) -> Option<Self> {
if self.is_comparable(other) {
let mut res = self.clone();
res.intersection_inplace(other);
Some(res)
} else {
None
}
}
/// Like `intersection` but in-place (updates `self`).
pub fn intersection_inplace(&mut self, other: &Self) -> Option<()> {
if self.is_comparable(other) {
for i in 0..self.bytes.len() {
self.bytes[i] &= other.bytes[i];
/// The amount to shift by, `n`, must be less than or equal to `self.len()`.
pub fn shift_up(&mut self, n: usize) -> Result<(), Error> {
if n <= self.len() {
// Shift the bits up (starting from the high indices to avoid overwriting)
for i in (n..self.len()).rev() {
self.set(i, self.get(i - n)?)?;
}
Some(())
} else {
None
}
}
/// Compute the union (binary-or) of this bitfield with another.
///
/// Returns `None` if `self.is_comparable(other) == false`.
pub fn union(&self, other: &Self) -> Option<Self> {
if self.is_comparable(other) {
let mut res = self.clone();
res.union_inplace(other);
Some(res)
} else {
None
}
}
/// Like `union` but in-place (updates `self`).
pub fn union_inplace(&mut self, other: &Self) -> Option<()> {
if self.is_comparable(other) {
for i in 0..self.bytes.len() {
self.bytes[i] |= other.bytes[i];
// Zero the low bits
for i in 0..n {
self.set(i, false).unwrap();
}
Some(())
Ok(())
} else {
None
Err(Error::OutOfBounds {
i: n,
len: self.len(),
})
}
}
/// Compute the difference (binary-minus) of this bitfield with another. Lengths must match.
///
/// Returns `None` if `self.is_comparable(other) == false`.
pub fn difference(&self, other: &Self) -> Option<Self> {
if self.is_comparable(other) {
let mut res = self.clone();
res.difference_inplace(other);
Some(res)
} else {
None
}
}
/// Like `difference` but in-place (updates `self`).
pub fn difference_inplace(&mut self, other: &Self) -> Option<()> {
if self.is_comparable(other) {
for i in 0..self.bytes.len() {
self.bytes[i] &= !other.bytes[i];
}
Some(())
} else {
None
}
}
/// Returns true if `self` and `other` have the same lengths and can be used in binary
/// comparison operations.
pub fn is_comparable(&self, other: &Self) -> bool {
(self.len() == other.len()) && (self.bytes.len() == other.bytes.len())
}
}
/// Returns the minimum required bytes to represent a given number of bits.
@@ -505,7 +501,11 @@ impl<N: Unsigned + Clone> Encode for Bitfield<Fixed<N>> {
impl<N: Unsigned + Clone> Decode for Bitfield<Fixed<N>> {
fn is_ssz_fixed_len() -> bool {
false
true
}
fn ssz_fixed_len() -> usize {
bytes_for_bit_len(N::to_usize())
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
@@ -573,106 +573,72 @@ impl<N: Unsigned + Clone> tree_hash::TreeHash for Bitfield<Variable<N>> {
}
fn tree_hash_root(&self) -> Vec<u8> {
// TODO: pad this out to max length.
self.as_ssz_bytes().tree_hash_root()
// Note: we use `as_slice` because it does _not_ have the length-delimiting bit set (or
// present).
let root = bitfield_bytes_tree_hash_root::<N>(self.as_slice());
tree_hash::mix_in_length(&root, self.len())
}
}
impl<N: Unsigned + Clone> tree_hash::TreeHash for Bitfield<Fixed<N>> {
fn tree_hash_type() -> tree_hash::TreeHashType {
// TODO: move this to be a vector.
tree_hash::TreeHashType::List
tree_hash::TreeHashType::Vector
}
fn tree_hash_packed_encoding(&self) -> Vec<u8> {
// TODO: move this to be a vector.
unreachable!("Vector should never be packed.")
}
fn tree_hash_packing_factor() -> usize {
// TODO: move this to be a vector.
unreachable!("Vector should never be packed.")
}
fn tree_hash_root(&self) -> Vec<u8> {
self.as_ssz_bytes().tree_hash_root()
bitfield_bytes_tree_hash_root::<N>(self.as_slice())
}
}
impl<N: Unsigned + Clone> cached_tree_hash::CachedTreeHash for Bitfield<Variable<N>> {
fn new_tree_hash_cache(
&self,
depth: usize,
_depth: usize,
) -> Result<cached_tree_hash::TreeHashCache, cached_tree_hash::Error> {
let bytes = self.clone().into_bytes();
let (mut cache, schema) = cached_tree_hash::vec::new_tree_hash_cache(&bytes, depth)?;
cache.add_length_nodes(schema.into_overlay(0).chunk_range(), bytes.len())?;
Ok(cache)
unimplemented!("CachedTreeHash is not implemented for BitList")
}
fn num_tree_hash_cache_chunks(&self) -> usize {
// Add two extra nodes to cater for the node before and after to allow mixing-in length.
cached_tree_hash::BTreeOverlay::new(self, 0, 0).num_chunks() + 2
unimplemented!("CachedTreeHash is not implemented for BitList")
}
fn tree_hash_cache_schema(&self, depth: usize) -> cached_tree_hash::BTreeSchema {
let bytes = self.clone().into_bytes();
cached_tree_hash::vec::produce_schema(&bytes, depth)
fn tree_hash_cache_schema(&self, _depth: usize) -> cached_tree_hash::BTreeSchema {
unimplemented!("CachedTreeHash is not implemented for BitList")
}
fn update_tree_hash_cache(
&self,
cache: &mut cached_tree_hash::TreeHashCache,
_cache: &mut cached_tree_hash::TreeHashCache,
) -> Result<(), cached_tree_hash::Error> {
let bytes = self.clone().into_bytes();
// Skip the length-mixed-in root node.
cache.chunk_index += 1;
// Update the cache, returning the new overlay.
let new_overlay = cached_tree_hash::vec::update_tree_hash_cache(&bytes, cache)?;
// Mix in length
cache.mix_in_length(new_overlay.chunk_range(), bytes.len())?;
// Skip an extra node to clear the length node.
cache.chunk_index += 1;
Ok(())
unimplemented!("CachedTreeHash is not implemented for BitList")
}
}
impl<N: Unsigned + Clone> cached_tree_hash::CachedTreeHash for Bitfield<Fixed<N>> {
fn new_tree_hash_cache(
&self,
depth: usize,
_depth: usize,
) -> Result<cached_tree_hash::TreeHashCache, cached_tree_hash::Error> {
let (cache, _schema) =
cached_tree_hash::vec::new_tree_hash_cache(&ssz::ssz_encode(self), depth)?;
Ok(cache)
unimplemented!("CachedTreeHash is not implemented for BitVec")
}
fn tree_hash_cache_schema(&self, depth: usize) -> cached_tree_hash::BTreeSchema {
let lengths = vec![
1;
cached_tree_hash::merkleize::num_unsanitized_leaves(bytes_for_bit_len(
N::to_usize()
))
];
cached_tree_hash::BTreeSchema::from_lengths(depth, lengths)
fn tree_hash_cache_schema(&self, _depth: usize) -> cached_tree_hash::BTreeSchema {
unimplemented!("CachedTreeHash is not implemented for BitVec")
}
fn update_tree_hash_cache(
&self,
cache: &mut cached_tree_hash::TreeHashCache,
_cache: &mut cached_tree_hash::TreeHashCache,
) -> Result<(), cached_tree_hash::Error> {
cached_tree_hash::vec::update_tree_hash_cache(&ssz::ssz_encode(self), cache)?;
Ok(())
unimplemented!("CachedTreeHash is not implemented for BitVec")
}
}
@@ -724,10 +690,12 @@ mod bitvector {
assert!(BitVector8::from_ssz_bytes(&[0b0000_0000]).is_ok());
assert!(BitVector8::from_ssz_bytes(&[1, 0b0000_0000]).is_err());
assert!(BitVector8::from_ssz_bytes(&[0b0000_0000, 1]).is_err());
assert!(BitVector8::from_ssz_bytes(&[0b0000_0001]).is_ok());
assert!(BitVector8::from_ssz_bytes(&[0b0000_0010]).is_ok());
assert!(BitVector8::from_ssz_bytes(&[0b0000_0001, 0b0000_0100]).is_err());
assert!(BitVector8::from_ssz_bytes(&[0b0000_0010, 0b0000_0100]).is_err());
assert!(BitVector8::from_ssz_bytes(&[0b0000_0100, 0b0000_0001]).is_err());
assert!(BitVector8::from_ssz_bytes(&[0b0000_0100, 0b0000_0010]).is_err());
assert!(BitVector8::from_ssz_bytes(&[0b0000_0100, 0b0000_0100]).is_err());
assert!(BitVector16::from_ssz_bytes(&[0b0000_0000]).is_err());
assert!(BitVector16::from_ssz_bytes(&[0b0000_0000, 0b0000_0000]).is_ok());
@@ -806,7 +774,7 @@ mod bitlist {
assert_eq!(
BitList8::with_capacity(8).unwrap().as_ssz_bytes(),
vec![0b0000_0001, 0b0000_0000],
vec![0b0000_0000, 0b0000_0001],
);
assert_eq!(
@@ -818,17 +786,17 @@ mod bitlist {
for i in 0..8 {
b.set(i, true).unwrap();
}
assert_eq!(b.as_ssz_bytes(), vec![0b0000_0001, 255]);
assert_eq!(b.as_ssz_bytes(), vec![255, 0b0000_0001]);
let mut b = BitList8::with_capacity(8).unwrap();
for i in 0..4 {
b.set(i, true).unwrap();
}
assert_eq!(b.as_ssz_bytes(), vec![0b0000_0001, 0b0000_1111]);
assert_eq!(b.as_ssz_bytes(), vec![0b0000_1111, 0b0000_0001]);
assert_eq!(
BitList16::with_capacity(16).unwrap().as_ssz_bytes(),
vec![0b0000_0001, 0b0000_0000, 0b0000_0000]
vec![0b0000_0000, 0b0000_0000, 0b0000_0001]
);
}
@@ -848,8 +816,9 @@ mod bitlist {
assert!(BitList8::from_ssz_bytes(&[0b0000_0001]).is_ok());
assert!(BitList8::from_ssz_bytes(&[0b0000_0010]).is_ok());
assert!(BitList8::from_ssz_bytes(&[0b0000_0001, 0b0000_0100]).is_ok());
assert!(BitList8::from_ssz_bytes(&[0b0000_0010, 0b0000_0100]).is_err());
assert!(BitList8::from_ssz_bytes(&[0b0000_0001, 0b0000_0001]).is_ok());
assert!(BitList8::from_ssz_bytes(&[0b0000_0001, 0b0000_0010]).is_err());
assert!(BitList8::from_ssz_bytes(&[0b0000_0001, 0b0000_0100]).is_err());
}
#[test]
@@ -919,19 +888,19 @@ mod bitlist {
assert!(BitList1024::from_raw_bytes(vec![0b0111_1111], 7).is_ok());
assert!(BitList1024::from_raw_bytes(vec![0b1111_1111], 8).is_ok());
assert!(BitList1024::from_raw_bytes(vec![0b0000_0001, 0b1111_1111], 9).is_ok());
assert!(BitList1024::from_raw_bytes(vec![0b0000_0011, 0b1111_1111], 10).is_ok());
assert!(BitList1024::from_raw_bytes(vec![0b0000_0111, 0b1111_1111], 11).is_ok());
assert!(BitList1024::from_raw_bytes(vec![0b0000_1111, 0b1111_1111], 12).is_ok());
assert!(BitList1024::from_raw_bytes(vec![0b0001_1111, 0b1111_1111], 13).is_ok());
assert!(BitList1024::from_raw_bytes(vec![0b0011_1111, 0b1111_1111], 14).is_ok());
assert!(BitList1024::from_raw_bytes(vec![0b0111_1111, 0b1111_1111], 15).is_ok());
assert!(BitList1024::from_raw_bytes(vec![0b1111_1111, 0b0000_0001], 9).is_ok());
assert!(BitList1024::from_raw_bytes(vec![0b1111_1111, 0b0000_0011], 10).is_ok());
assert!(BitList1024::from_raw_bytes(vec![0b1111_1111, 0b0000_0111], 11).is_ok());
assert!(BitList1024::from_raw_bytes(vec![0b1111_1111, 0b0000_1111], 12).is_ok());
assert!(BitList1024::from_raw_bytes(vec![0b1111_1111, 0b0001_1111], 13).is_ok());
assert!(BitList1024::from_raw_bytes(vec![0b1111_1111, 0b0011_1111], 14).is_ok());
assert!(BitList1024::from_raw_bytes(vec![0b1111_1111, 0b0111_1111], 15).is_ok());
assert!(BitList1024::from_raw_bytes(vec![0b1111_1111, 0b1111_1111], 16).is_ok());
for i in 0..8 {
assert!(BitList1024::from_raw_bytes(vec![], i).is_err());
assert!(BitList1024::from_raw_bytes(vec![0b1111_1111], i).is_err());
assert!(BitList1024::from_raw_bytes(vec![0b1111_1110, 0b0000_0000], i).is_err());
assert!(BitList1024::from_raw_bytes(vec![0b0000_0000, 0b1111_1110], i).is_err());
}
assert!(BitList1024::from_raw_bytes(vec![0b0000_0001], 0).is_err());
@@ -945,13 +914,13 @@ mod bitlist {
assert!(BitList1024::from_raw_bytes(vec![0b0111_1111], 6).is_err());
assert!(BitList1024::from_raw_bytes(vec![0b1111_1111], 7).is_err());
assert!(BitList1024::from_raw_bytes(vec![0b0000_0001, 0b1111_1111], 8).is_err());
assert!(BitList1024::from_raw_bytes(vec![0b0000_0011, 0b1111_1111], 9).is_err());
assert!(BitList1024::from_raw_bytes(vec![0b0000_0111, 0b1111_1111], 10).is_err());
assert!(BitList1024::from_raw_bytes(vec![0b0000_1111, 0b1111_1111], 11).is_err());
assert!(BitList1024::from_raw_bytes(vec![0b0001_1111, 0b1111_1111], 12).is_err());
assert!(BitList1024::from_raw_bytes(vec![0b0011_1111, 0b1111_1111], 13).is_err());
assert!(BitList1024::from_raw_bytes(vec![0b0111_1111, 0b1111_1111], 14).is_err());
assert!(BitList1024::from_raw_bytes(vec![0b1111_1111, 0b0000_0001], 8).is_err());
assert!(BitList1024::from_raw_bytes(vec![0b1111_1111, 0b0000_0011], 9).is_err());
assert!(BitList1024::from_raw_bytes(vec![0b1111_1111, 0b0000_0111], 10).is_err());
assert!(BitList1024::from_raw_bytes(vec![0b1111_1111, 0b0000_1111], 11).is_err());
assert!(BitList1024::from_raw_bytes(vec![0b1111_1111, 0b0001_1111], 12).is_err());
assert!(BitList1024::from_raw_bytes(vec![0b1111_1111, 0b0011_1111], 13).is_err());
assert!(BitList1024::from_raw_bytes(vec![0b1111_1111, 0b0111_1111], 14).is_err());
assert!(BitList1024::from_raw_bytes(vec![0b1111_1111, 0b1111_1111], 15).is_err());
}
@@ -1006,47 +975,47 @@ mod bitlist {
bitfield.set(0, true).unwrap();
assert_eq!(
bitfield.clone().into_raw_bytes(),
vec![0b0000_0000, 0b0000_0001]
vec![0b0000_0001, 0b0000_0000]
);
bitfield.set(1, true).unwrap();
assert_eq!(
bitfield.clone().into_raw_bytes(),
vec![0b0000_0000, 0b0000_0011]
vec![0b0000_0011, 0b0000_0000]
);
bitfield.set(2, true).unwrap();
assert_eq!(
bitfield.clone().into_raw_bytes(),
vec![0b0000_0000, 0b0000_0111]
vec![0b0000_0111, 0b0000_0000]
);
bitfield.set(3, true).unwrap();
assert_eq!(
bitfield.clone().into_raw_bytes(),
vec![0b0000_0000, 0b0000_1111]
vec![0b0000_1111, 0b0000_0000]
);
bitfield.set(4, true).unwrap();
assert_eq!(
bitfield.clone().into_raw_bytes(),
vec![0b0000_0000, 0b0001_1111]
vec![0b0001_1111, 0b0000_0000]
);
bitfield.set(5, true).unwrap();
assert_eq!(
bitfield.clone().into_raw_bytes(),
vec![0b0000_0000, 0b0011_1111]
vec![0b0011_1111, 0b0000_0000]
);
bitfield.set(6, true).unwrap();
assert_eq!(
bitfield.clone().into_raw_bytes(),
vec![0b0000_0000, 0b0111_1111]
vec![0b0111_1111, 0b0000_0000]
);
bitfield.set(7, true).unwrap();
assert_eq!(
bitfield.clone().into_raw_bytes(),
vec![0b0000_0000, 0b1111_1111]
vec![0b1111_1111, 0b0000_0000]
);
bitfield.set(8, true).unwrap();
assert_eq!(
bitfield.clone().into_raw_bytes(),
vec![0b0000_0001, 0b1111_1111]
vec![0b1111_1111, 0b0000_0001]
);
}
@@ -1058,14 +1027,14 @@ mod bitlist {
);
assert_eq!(
BitList1024::from_raw_bytes(vec![0b0000_000, 0b0000_0001], 16)
BitList1024::from_raw_bytes(vec![0b0000_0001, 0b0000_0000], 16)
.unwrap()
.highest_set_bit(),
Some(0)
);
assert_eq!(
BitList1024::from_raw_bytes(vec![0b0000_000, 0b0000_0010], 16)
BitList1024::from_raw_bytes(vec![0b0000_0010, 0b0000_0000], 16)
.unwrap()
.highest_set_bit(),
Some(1)
@@ -1079,7 +1048,7 @@ mod bitlist {
);
assert_eq!(
BitList1024::from_raw_bytes(vec![0b1000_0000, 0b0000_0000], 16)
BitList1024::from_raw_bytes(vec![0b0000_0000, 0b1000_0000], 16)
.unwrap()
.highest_set_bit(),
Some(15)
@@ -1092,13 +1061,30 @@ mod bitlist {
let b = BitList1024::from_raw_bytes(vec![0b1011, 0b1001], 16).unwrap();
let c = BitList1024::from_raw_bytes(vec![0b1000, 0b0001], 16).unwrap();
assert_eq!(a.intersection(&b).unwrap(), c);
assert_eq!(b.intersection(&a).unwrap(), c);
assert_eq!(a.intersection(&c).unwrap(), c);
assert_eq!(b.intersection(&c).unwrap(), c);
assert_eq!(a.intersection(&a).unwrap(), a);
assert_eq!(b.intersection(&b).unwrap(), b);
assert_eq!(c.intersection(&c).unwrap(), c);
assert_eq!(a.intersection(&b), c);
assert_eq!(b.intersection(&a), c);
assert_eq!(a.intersection(&c), c);
assert_eq!(b.intersection(&c), c);
assert_eq!(a.intersection(&a), a);
assert_eq!(b.intersection(&b), b);
assert_eq!(c.intersection(&c), c);
}
#[test]
fn intersection_diff_length() {
let a = BitList1024::from_bytes(vec![0b0010_1110, 0b0010_1011]).unwrap();
let b = BitList1024::from_bytes(vec![0b0010_1101, 0b0000_0001]).unwrap();
let c = BitList1024::from_bytes(vec![0b0010_1100, 0b0000_0001]).unwrap();
let d = BitList1024::from_bytes(vec![0b0010_1110, 0b1111_1111, 0b1111_1111]).unwrap();
assert_eq!(a.len(), 13);
assert_eq!(b.len(), 8);
assert_eq!(c.len(), 8);
assert_eq!(d.len(), 23);
assert_eq!(a.intersection(&b), c);
assert_eq!(b.intersection(&a), c);
assert_eq!(a.intersection(&d), a);
assert_eq!(d.intersection(&a), a);
}
#[test]
@@ -1107,11 +1093,25 @@ mod bitlist {
let b = BitList1024::from_raw_bytes(vec![0b1011, 0b1001], 16).unwrap();
let c = BitList1024::from_raw_bytes(vec![0b1111, 0b1001], 16).unwrap();
assert_eq!(a.union(&b).unwrap(), c);
assert_eq!(b.union(&a).unwrap(), c);
assert_eq!(a.union(&a).unwrap(), a);
assert_eq!(b.union(&b).unwrap(), b);
assert_eq!(c.union(&c).unwrap(), c);
assert_eq!(a.union(&b), c);
assert_eq!(b.union(&a), c);
assert_eq!(a.union(&a), a);
assert_eq!(b.union(&b), b);
assert_eq!(c.union(&c), c);
}
#[test]
fn union_diff_length() {
let a = BitList1024::from_bytes(vec![0b0010_1011, 0b0010_1110]).unwrap();
let b = BitList1024::from_bytes(vec![0b0000_0001, 0b0010_1101]).unwrap();
let c = BitList1024::from_bytes(vec![0b0010_1011, 0b0010_1111]).unwrap();
let d = BitList1024::from_bytes(vec![0b0010_1011, 0b1011_1110, 0b1000_1101]).unwrap();
assert_eq!(a.len(), c.len());
assert_eq!(a.union(&b), c);
assert_eq!(b.union(&a), c);
assert_eq!(a.union(&d), d);
assert_eq!(d.union(&a), d);
}
#[test]
@@ -1121,9 +1121,44 @@ mod bitlist {
let a_b = BitList1024::from_raw_bytes(vec![0b0100, 0b0000], 16).unwrap();
let b_a = BitList1024::from_raw_bytes(vec![0b0011, 0b1000], 16).unwrap();
assert_eq!(a.difference(&b).unwrap(), a_b);
assert_eq!(b.difference(&a).unwrap(), b_a);
assert!(a.difference(&a).unwrap().is_zero());
assert_eq!(a.difference(&b), a_b);
assert_eq!(b.difference(&a), b_a);
assert!(a.difference(&a).is_zero());
}
#[test]
fn difference_diff_length() {
let a = BitList1024::from_raw_bytes(vec![0b0110, 0b1100, 0b0011], 24).unwrap();
let b = BitList1024::from_raw_bytes(vec![0b1011, 0b1001], 16).unwrap();
let a_b = BitList1024::from_raw_bytes(vec![0b0100, 0b0100, 0b0011], 24).unwrap();
let b_a = BitList1024::from_raw_bytes(vec![0b1001, 0b0001], 16).unwrap();
assert_eq!(a.difference(&b), a_b);
assert_eq!(b.difference(&a), b_a);
}
#[test]
fn shift_up() {
let mut a = BitList1024::from_raw_bytes(vec![0b1100_1111, 0b1101_0110], 16).unwrap();
let mut b = BitList1024::from_raw_bytes(vec![0b1001_1110, 0b1010_1101], 16).unwrap();
a.shift_up(1).unwrap();
assert_eq!(a, b);
a.shift_up(15).unwrap();
assert!(a.is_zero());
b.shift_up(16).unwrap();
assert!(b.is_zero());
assert!(b.shift_up(17).is_err());
}
#[test]
fn num_set_bits() {
let a = BitList1024::from_raw_bytes(vec![0b1100, 0b0001], 16).unwrap();
let b = BitList1024::from_raw_bytes(vec![0b1011, 0b1001], 16).unwrap();
assert_eq!(a.num_set_bits(), 3);
assert_eq!(b.num_set_bits(), 5);
}
#[test]
+146 -78
View File
@@ -1,3 +1,4 @@
use crate::tree_hash::vec_tree_hash_root;
use crate::Error;
use serde_derive::{Deserialize, Serialize};
use std::marker::PhantomData;
@@ -66,6 +67,17 @@ impl<T, N: Unsigned> FixedVector<T, N> {
}
}
/// Create a new vector filled with clones of `elem`.
pub fn from_elem(elem: T) -> Self
where
T: Clone,
{
Self {
vec: vec![elem; N::to_usize()],
_phantom: PhantomData,
}
}
/// Identical to `self.capacity`, returns the type-level constant length.
///
/// Exists for compatibility with `Vec`.
@@ -134,67 +146,6 @@ impl<T, N: Unsigned> Deref for FixedVector<T, N> {
}
}
#[cfg(test)]
mod test {
use super::*;
use typenum::*;
#[test]
fn new() {
let vec = vec![42; 5];
let fixed: Result<FixedVector<u64, U4>, _> = FixedVector::new(vec.clone());
assert!(fixed.is_err());
let vec = vec![42; 3];
let fixed: Result<FixedVector<u64, U4>, _> = FixedVector::new(vec.clone());
assert!(fixed.is_err());
let vec = vec![42; 4];
let fixed: Result<FixedVector<u64, U4>, _> = FixedVector::new(vec.clone());
assert!(fixed.is_ok());
}
#[test]
fn indexing() {
let vec = vec![1, 2];
let mut fixed: FixedVector<u64, U8192> = vec.clone().into();
assert_eq!(fixed[0], 1);
assert_eq!(&fixed[0..1], &vec[0..1]);
assert_eq!((&fixed[..]).len(), 8192);
fixed[1] = 3;
assert_eq!(fixed[1], 3);
}
#[test]
fn length() {
let vec = vec![42; 5];
let fixed: FixedVector<u64, U4> = FixedVector::from(vec.clone());
assert_eq!(&fixed[..], &vec[0..4]);
let vec = vec![42; 3];
let fixed: FixedVector<u64, U4> = FixedVector::from(vec.clone());
assert_eq!(&fixed[0..3], &vec[..]);
assert_eq!(&fixed[..], &vec![42, 42, 42, 0][..]);
let vec = vec![];
let fixed: FixedVector<u64, U4> = FixedVector::from(vec.clone());
assert_eq!(&fixed[..], &vec![0, 0, 0, 0][..]);
}
#[test]
fn deref() {
let vec = vec![0, 2, 4, 6];
let fixed: FixedVector<u64, U4> = FixedVector::from(vec);
assert_eq!(fixed.get(0), Some(&0));
assert_eq!(fixed.get(3), Some(&6));
assert_eq!(fixed.get(4), None);
}
}
impl<T, N: Unsigned> tree_hash::TreeHash for FixedVector<T, N>
where
T: tree_hash::TreeHash,
@@ -212,7 +163,7 @@ where
}
fn tree_hash_root(&self) -> Vec<u8> {
tree_hash::impls::vec_tree_hash_root(&self.vec)
vec_tree_hash_root::<T, N>(&self.vec)
}
}
@@ -222,24 +173,20 @@ where
{
fn new_tree_hash_cache(
&self,
depth: usize,
_depth: usize,
) -> Result<cached_tree_hash::TreeHashCache, cached_tree_hash::Error> {
let (cache, _overlay) = cached_tree_hash::vec::new_tree_hash_cache(&self.vec, depth)?;
Ok(cache)
unimplemented!("CachedTreeHash is not implemented for FixedVector")
}
fn tree_hash_cache_schema(&self, depth: usize) -> cached_tree_hash::BTreeSchema {
cached_tree_hash::vec::produce_schema(&self.vec, depth)
fn tree_hash_cache_schema(&self, _depth: usize) -> cached_tree_hash::BTreeSchema {
unimplemented!("CachedTreeHash is not implemented for FixedVector")
}
fn update_tree_hash_cache(
&self,
cache: &mut cached_tree_hash::TreeHashCache,
_cache: &mut cached_tree_hash::TreeHashCache,
) -> Result<(), cached_tree_hash::Error> {
cached_tree_hash::vec::update_tree_hash_cache(&self.vec, cache)?;
Ok(())
unimplemented!("CachedTreeHash is not implemented for FixedVector")
}
}
@@ -310,26 +257,147 @@ where
}
#[cfg(test)]
mod ssz_tests {
mod test {
use super::*;
use ssz::*;
use tree_hash::{merkle_root, TreeHash};
use tree_hash_derive::TreeHash;
use typenum::*;
#[test]
fn encode() {
fn new() {
let vec = vec![42; 5];
let fixed: Result<FixedVector<u64, U4>, _> = FixedVector::new(vec.clone());
assert!(fixed.is_err());
let vec = vec![42; 3];
let fixed: Result<FixedVector<u64, U4>, _> = FixedVector::new(vec.clone());
assert!(fixed.is_err());
let vec = vec![42; 4];
let fixed: Result<FixedVector<u64, U4>, _> = FixedVector::new(vec.clone());
assert!(fixed.is_ok());
}
#[test]
fn indexing() {
let vec = vec![1, 2];
let mut fixed: FixedVector<u64, U8192> = vec.clone().into();
assert_eq!(fixed[0], 1);
assert_eq!(&fixed[0..1], &vec[0..1]);
assert_eq!((&fixed[..]).len(), 8192);
fixed[1] = 3;
assert_eq!(fixed[1], 3);
}
#[test]
fn length() {
let vec = vec![42; 5];
let fixed: FixedVector<u64, U4> = FixedVector::from(vec.clone());
assert_eq!(&fixed[..], &vec[0..4]);
let vec = vec![42; 3];
let fixed: FixedVector<u64, U4> = FixedVector::from(vec.clone());
assert_eq!(&fixed[0..3], &vec[..]);
assert_eq!(&fixed[..], &vec![42, 42, 42, 0][..]);
let vec = vec![];
let fixed: FixedVector<u64, U4> = FixedVector::from(vec.clone());
assert_eq!(&fixed[..], &vec![0, 0, 0, 0][..]);
}
#[test]
fn deref() {
let vec = vec![0, 2, 4, 6];
let fixed: FixedVector<u64, U4> = FixedVector::from(vec);
assert_eq!(fixed.get(0), Some(&0));
assert_eq!(fixed.get(3), Some(&6));
assert_eq!(fixed.get(4), None);
}
#[test]
fn ssz_encode() {
let vec: FixedVector<u16, U2> = vec![0; 2].into();
assert_eq!(vec.as_ssz_bytes(), vec![0, 0, 0, 0]);
assert_eq!(<FixedVector<u16, U2> as Encode>::ssz_fixed_len(), 4);
}
fn round_trip<T: Encode + Decode + std::fmt::Debug + PartialEq>(item: T) {
fn ssz_round_trip<T: Encode + Decode + std::fmt::Debug + PartialEq>(item: T) {
let encoded = &item.as_ssz_bytes();
assert_eq!(T::from_ssz_bytes(&encoded), Ok(item));
}
#[test]
fn u16_len_8() {
round_trip::<FixedVector<u16, U8>>(vec![42; 8].into());
round_trip::<FixedVector<u16, U8>>(vec![0; 8].into());
fn ssz_round_trip_u16_len_8() {
ssz_round_trip::<FixedVector<u16, U8>>(vec![42; 8].into());
ssz_round_trip::<FixedVector<u16, U8>>(vec![0; 8].into());
}
#[test]
fn tree_hash_u8() {
let fixed: FixedVector<u8, U0> = FixedVector::from(vec![]);
assert_eq!(fixed.tree_hash_root(), merkle_root(&[0; 8], 0));
let fixed: FixedVector<u8, U1> = FixedVector::from(vec![0; 1]);
assert_eq!(fixed.tree_hash_root(), merkle_root(&[0; 8], 0));
let fixed: FixedVector<u8, U8> = FixedVector::from(vec![0; 8]);
assert_eq!(fixed.tree_hash_root(), merkle_root(&[0; 8], 0));
let fixed: FixedVector<u8, U16> = FixedVector::from(vec![42; 16]);
assert_eq!(fixed.tree_hash_root(), merkle_root(&[42; 16], 0));
let source: Vec<u8> = (0..16).collect();
let fixed: FixedVector<u8, U16> = FixedVector::from(source.clone());
assert_eq!(fixed.tree_hash_root(), merkle_root(&source, 0));
}
#[derive(Clone, Copy, TreeHash, Default)]
struct A {
a: u32,
b: u32,
}
fn repeat(input: &[u8], n: usize) -> Vec<u8> {
let mut output = vec![];
for _ in 0..n {
output.append(&mut input.to_vec());
}
output
}
#[test]
fn tree_hash_composite() {
let a = A { a: 0, b: 1 };
let fixed: FixedVector<A, U0> = FixedVector::from(vec![]);
assert_eq!(fixed.tree_hash_root(), merkle_root(&[0; 32], 0));
let fixed: FixedVector<A, U1> = FixedVector::from(vec![a]);
assert_eq!(fixed.tree_hash_root(), merkle_root(&a.tree_hash_root(), 0));
let fixed: FixedVector<A, U8> = FixedVector::from(vec![a; 8]);
assert_eq!(
fixed.tree_hash_root(),
merkle_root(&repeat(&a.tree_hash_root(), 8), 0)
);
let fixed: FixedVector<A, U13> = FixedVector::from(vec![a; 13]);
assert_eq!(
fixed.tree_hash_root(),
merkle_root(&repeat(&a.tree_hash_root(), 13), 0)
);
let fixed: FixedVector<A, U16> = FixedVector::from(vec![a; 16]);
assert_eq!(
fixed.tree_hash_root(),
merkle_root(&repeat(&a.tree_hash_root(), 16), 0)
);
}
}
+5
View File
@@ -8,6 +8,10 @@
//! These structs are required as SSZ serialization and Merklization rely upon type-level lengths
//! for padding and verification.
//!
//! Adheres to the Ethereum 2.0 [SSZ
//! specification](https://github.com/ethereum/eth2.0-specs/blob/v0.8.1/specs/simple-serialize.md)
//! at v0.8.1 .
//!
//! ## Example
//! ```
//! use ssz_types::*;
@@ -36,6 +40,7 @@
#[macro_use]
mod bitfield;
mod fixed_vector;
mod tree_hash;
mod variable_list;
pub use bitfield::{BitList, BitVector, Bitfield};
+48
View File
@@ -0,0 +1,48 @@
use tree_hash::{merkle_root, TreeHash, TreeHashType, BYTES_PER_CHUNK};
use typenum::Unsigned;
/// A helper function providing common functionality between the `TreeHash` implementations for
/// `FixedVector` and `VariableList`.
pub fn vec_tree_hash_root<T, N>(vec: &[T]) -> Vec<u8>
where
T: TreeHash,
N: Unsigned,
{
let (leaves, minimum_chunk_count) = match T::tree_hash_type() {
TreeHashType::Basic => {
let mut leaves =
Vec::with_capacity((BYTES_PER_CHUNK / T::tree_hash_packing_factor()) * vec.len());
for item in vec {
leaves.append(&mut item.tree_hash_packed_encoding());
}
let values_per_chunk = T::tree_hash_packing_factor();
let minimum_chunk_count = (N::to_usize() + values_per_chunk - 1) / values_per_chunk;
(leaves, minimum_chunk_count)
}
TreeHashType::Container | TreeHashType::List | TreeHashType::Vector => {
let mut leaves = Vec::with_capacity(vec.len() * BYTES_PER_CHUNK);
for item in vec {
leaves.append(&mut item.tree_hash_root())
}
let minimum_chunk_count = N::to_usize();
(leaves, minimum_chunk_count)
}
};
merkle_root(&leaves, minimum_chunk_count)
}
/// A helper function providing common functionality for finding the Merkle root of some bytes that
/// represent a bitfield.
pub fn bitfield_bytes_tree_hash_root<N: Unsigned>(bytes: &[u8]) -> Vec<u8> {
let byte_size = (N::to_usize() + 7) / 8;
let minimum_chunk_count = (byte_size + BYTES_PER_CHUNK - 1) / BYTES_PER_CHUNK;
merkle_root(bytes, minimum_chunk_count)
}
+218 -93
View File
@@ -1,7 +1,8 @@
use crate::tree_hash::vec_tree_hash_root;
use crate::Error;
use serde_derive::{Deserialize, Serialize};
use std::marker::PhantomData;
use std::ops::{Deref, Index, IndexMut};
use std::ops::{Deref, DerefMut, Index, IndexMut};
use std::slice::SliceIndex;
use typenum::Unsigned;
@@ -68,6 +69,14 @@ impl<T, N: Unsigned> VariableList<T, N> {
}
}
/// Create an empty list.
pub fn empty() -> Self {
Self {
vec: vec![],
_phantom: PhantomData,
}
}
/// Returns the number of values presently in `self`.
pub fn len(&self) -> usize {
self.vec.len()
@@ -99,7 +108,7 @@ impl<T, N: Unsigned> VariableList<T, N> {
}
}
impl<T: Default, N: Unsigned> From<Vec<T>> for VariableList<T, N> {
impl<T, N: Unsigned> From<Vec<T>> for VariableList<T, N> {
fn from(mut vec: Vec<T>) -> Self {
vec.truncate(N::to_usize());
@@ -149,9 +158,109 @@ impl<T, N: Unsigned> Deref for VariableList<T, N> {
}
}
impl<T, N: Unsigned> DerefMut for VariableList<T, N> {
fn deref_mut(&mut self) -> &mut [T] {
&mut self.vec[..]
}
}
impl<'a, T, N: Unsigned> IntoIterator for &'a VariableList<T, N> {
type Item = &'a T;
type IntoIter = std::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<T, N: Unsigned> tree_hash::TreeHash for VariableList<T, N>
where
T: tree_hash::TreeHash,
{
fn tree_hash_type() -> tree_hash::TreeHashType {
tree_hash::TreeHashType::List
}
fn tree_hash_packed_encoding(&self) -> Vec<u8> {
unreachable!("List should never be packed.")
}
fn tree_hash_packing_factor() -> usize {
unreachable!("List should never be packed.")
}
fn tree_hash_root(&self) -> Vec<u8> {
let root = vec_tree_hash_root::<T, N>(&self.vec);
tree_hash::mix_in_length(&root, self.len())
}
}
impl<T, N: Unsigned> cached_tree_hash::CachedTreeHash for VariableList<T, N>
where
T: cached_tree_hash::CachedTreeHash + tree_hash::TreeHash,
{
fn new_tree_hash_cache(
&self,
_depth: usize,
) -> Result<cached_tree_hash::TreeHashCache, cached_tree_hash::Error> {
unimplemented!("CachedTreeHash is not implemented for VariableList")
}
fn tree_hash_cache_schema(&self, _depth: usize) -> cached_tree_hash::BTreeSchema {
unimplemented!("CachedTreeHash is not implemented for VariableList")
}
fn update_tree_hash_cache(
&self,
_cache: &mut cached_tree_hash::TreeHashCache,
) -> Result<(), cached_tree_hash::Error> {
unimplemented!("CachedTreeHash is not implemented for VariableList")
}
}
impl<T, N: Unsigned> ssz::Encode for VariableList<T, N>
where
T: ssz::Encode,
{
fn is_ssz_fixed_len() -> bool {
<Vec<T>>::is_ssz_fixed_len()
}
fn ssz_fixed_len() -> usize {
<Vec<T>>::ssz_fixed_len()
}
fn ssz_append(&self, buf: &mut Vec<u8>) {
self.vec.ssz_append(buf)
}
}
impl<T, N: Unsigned> ssz::Decode for VariableList<T, N>
where
T: ssz::Decode,
{
fn is_ssz_fixed_len() -> bool {
<Vec<T>>::is_ssz_fixed_len()
}
fn ssz_fixed_len() -> usize {
<Vec<T>>::ssz_fixed_len()
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
let vec = <Vec<T>>::from_ssz_bytes(bytes)?;
Self::new(vec).map_err(|e| ssz::DecodeError::BytesInvalid(format!("VariableList {:?}", e)))
}
}
#[cfg(test)]
mod test {
use super::*;
use ssz::*;
use tree_hash::{merkle_root, TreeHash};
use tree_hash_derive::TreeHash;
use typenum::*;
#[test]
@@ -208,97 +317,6 @@ mod test {
assert_eq!(fixed.get(3), Some(&6));
assert_eq!(fixed.get(4), None);
}
}
impl<T, N: Unsigned> tree_hash::TreeHash for VariableList<T, N>
where
T: tree_hash::TreeHash,
{
fn tree_hash_type() -> tree_hash::TreeHashType {
tree_hash::TreeHashType::Vector
}
fn tree_hash_packed_encoding(&self) -> Vec<u8> {
unreachable!("Vector should never be packed.")
}
fn tree_hash_packing_factor() -> usize {
unreachable!("Vector should never be packed.")
}
fn tree_hash_root(&self) -> Vec<u8> {
tree_hash::impls::vec_tree_hash_root(&self.vec)
}
}
impl<T, N: Unsigned> cached_tree_hash::CachedTreeHash for VariableList<T, N>
where
T: cached_tree_hash::CachedTreeHash + tree_hash::TreeHash,
{
fn new_tree_hash_cache(
&self,
depth: usize,
) -> Result<cached_tree_hash::TreeHashCache, cached_tree_hash::Error> {
let (cache, _overlay) = cached_tree_hash::vec::new_tree_hash_cache(&self.vec, depth)?;
Ok(cache)
}
fn tree_hash_cache_schema(&self, depth: usize) -> cached_tree_hash::BTreeSchema {
cached_tree_hash::vec::produce_schema(&self.vec, depth)
}
fn update_tree_hash_cache(
&self,
cache: &mut cached_tree_hash::TreeHashCache,
) -> Result<(), cached_tree_hash::Error> {
cached_tree_hash::vec::update_tree_hash_cache(&self.vec, cache)?;
Ok(())
}
}
impl<T, N: Unsigned> ssz::Encode for VariableList<T, N>
where
T: ssz::Encode,
{
fn is_ssz_fixed_len() -> bool {
<Vec<T>>::is_ssz_fixed_len()
}
fn ssz_fixed_len() -> usize {
<Vec<T>>::ssz_fixed_len()
}
fn ssz_append(&self, buf: &mut Vec<u8>) {
self.vec.ssz_append(buf)
}
}
impl<T, N: Unsigned> ssz::Decode for VariableList<T, N>
where
T: ssz::Decode + Default,
{
fn is_ssz_fixed_len() -> bool {
<Vec<T>>::is_ssz_fixed_len()
}
fn ssz_fixed_len() -> usize {
<Vec<T>>::ssz_fixed_len()
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
let vec = <Vec<T>>::from_ssz_bytes(bytes)?;
Self::new(vec).map_err(|e| ssz::DecodeError::BytesInvalid(format!("VariableList {:?}", e)))
}
}
#[cfg(test)]
mod tests {
use super::*;
use ssz::*;
use typenum::*;
#[test]
fn encode() {
@@ -317,4 +335,111 @@ mod tests {
round_trip::<VariableList<u16, U8>>(vec![42; 8].into());
round_trip::<VariableList<u16, U8>>(vec![0; 8].into());
}
fn root_with_length(bytes: &[u8], len: usize) -> Vec<u8> {
let root = merkle_root(bytes, 0);
tree_hash::mix_in_length(&root, len)
}
#[test]
fn tree_hash_u8() {
let fixed: VariableList<u8, U0> = VariableList::from(vec![]);
assert_eq!(fixed.tree_hash_root(), root_with_length(&[0; 8], 0));
for i in 0..=1 {
let fixed: VariableList<u8, U1> = VariableList::from(vec![0; i]);
assert_eq!(fixed.tree_hash_root(), root_with_length(&vec![0; i], i));
}
for i in 0..=8 {
let fixed: VariableList<u8, U8> = VariableList::from(vec![0; i]);
assert_eq!(fixed.tree_hash_root(), root_with_length(&vec![0; i], i));
}
for i in 0..=13 {
let fixed: VariableList<u8, U13> = VariableList::from(vec![0; i]);
assert_eq!(fixed.tree_hash_root(), root_with_length(&vec![0; i], i));
}
for i in 0..=16 {
let fixed: VariableList<u8, U16> = VariableList::from(vec![0; i]);
assert_eq!(fixed.tree_hash_root(), root_with_length(&vec![0; i], i));
}
let source: Vec<u8> = (0..16).collect();
let fixed: VariableList<u8, U16> = VariableList::from(source.clone());
assert_eq!(fixed.tree_hash_root(), root_with_length(&source, 16));
}
#[derive(Clone, Copy, TreeHash, Default)]
struct A {
a: u32,
b: u32,
}
fn repeat(input: &[u8], n: usize) -> Vec<u8> {
let mut output = vec![];
for _ in 0..n {
output.append(&mut input.to_vec());
}
output
}
fn padded_root_with_length(bytes: &[u8], len: usize, min_nodes: usize) -> Vec<u8> {
let root = merkle_root(bytes, min_nodes);
tree_hash::mix_in_length(&root, len)
}
#[test]
fn tree_hash_composite() {
let a = A { a: 0, b: 1 };
let fixed: VariableList<A, U0> = VariableList::from(vec![]);
assert_eq!(
fixed.tree_hash_root(),
padded_root_with_length(&[0; 32], 0, 0),
);
for i in 0..=1 {
let fixed: VariableList<A, U1> = VariableList::from(vec![a; i]);
assert_eq!(
fixed.tree_hash_root(),
padded_root_with_length(&repeat(&a.tree_hash_root(), i), i, 1),
"U1 {}",
i
);
}
for i in 0..=8 {
let fixed: VariableList<A, U8> = VariableList::from(vec![a; i]);
assert_eq!(
fixed.tree_hash_root(),
padded_root_with_length(&repeat(&a.tree_hash_root(), i), i, 8),
"U8 {}",
i
);
}
for i in 0..=13 {
let fixed: VariableList<A, U13> = VariableList::from(vec![a; i]);
assert_eq!(
fixed.tree_hash_root(),
padded_root_with_length(&repeat(&a.tree_hash_root(), i), i, 13),
"U13 {}",
i
);
}
for i in 0..=16 {
let fixed: VariableList<A, U16> = VariableList::from(vec![a; i]);
assert_eq!(
fixed.tree_hash_root(),
padded_root_with_length(&repeat(&a.tree_hash_root(), i), i, 16),
"U16 {}",
i
);
}
}
}
+8 -41
View File
@@ -1,7 +1,5 @@
use super::*;
use crate::merkle_root;
use ethereum_types::H256;
use hashing::hash;
use int_to_bytes::int_to_bytes32;
macro_rules! impl_for_bitsize {
@@ -67,7 +65,7 @@ macro_rules! impl_for_u8_array {
}
fn tree_hash_root(&self) -> Vec<u8> {
merkle_root(&self[..])
merkle_root(&self[..], 0)
}
}
};
@@ -90,10 +88,12 @@ impl TreeHash for H256 {
}
fn tree_hash_root(&self) -> Vec<u8> {
merkle_root(&self.as_bytes().to_vec())
merkle_root(&self.as_bytes().to_vec(), 0)
}
}
// TODO: this implementation always panics, it only exists to allow us to compile whilst
// refactoring tree hash. Should be removed.
macro_rules! impl_for_list {
($type: ty) => {
impl<T> TreeHash for $type
@@ -101,23 +101,19 @@ macro_rules! impl_for_list {
T: TreeHash,
{
fn tree_hash_type() -> TreeHashType {
TreeHashType::List
unimplemented!("TreeHash is not implemented for Vec or slice")
}
fn tree_hash_packed_encoding(&self) -> Vec<u8> {
unreachable!("List should never be packed.")
unimplemented!("TreeHash is not implemented for Vec or slice")
}
fn tree_hash_packing_factor() -> usize {
unreachable!("List should never be packed.")
unimplemented!("TreeHash is not implemented for Vec or slice")
}
fn tree_hash_root(&self) -> Vec<u8> {
let mut root_and_len = Vec::with_capacity(HASHSIZE * 2);
root_and_len.append(&mut vec_tree_hash_root(self));
root_and_len.append(&mut int_to_bytes32(self.len() as u64));
hash(&root_and_len)
unimplemented!("TreeHash is not implemented for Vec or slice")
}
}
};
@@ -126,35 +122,6 @@ macro_rules! impl_for_list {
impl_for_list!(Vec<T>);
impl_for_list!(&[T]);
pub fn vec_tree_hash_root<T>(vec: &[T]) -> Vec<u8>
where
T: TreeHash,
{
let leaves = match T::tree_hash_type() {
TreeHashType::Basic => {
let mut leaves =
Vec::with_capacity((HASHSIZE / T::tree_hash_packing_factor()) * vec.len());
for item in vec {
leaves.append(&mut item.tree_hash_packed_encoding());
}
leaves
}
TreeHashType::Container | TreeHashType::List | TreeHashType::Vector => {
let mut leaves = Vec::with_capacity(vec.len() * HASHSIZE);
for item in vec {
leaves.append(&mut item.tree_hash_root())
}
leaves
}
};
merkle_root(&leaves)
}
#[cfg(test)]
mod test {
use super::*;
+35 -5
View File
@@ -8,15 +8,28 @@ mod merkleize_standard;
pub use merkleize_padded::merkleize_padded;
pub use merkleize_standard::merkleize_standard;
/// Alias to `merkleize_padded(&bytes, 0)`
pub fn merkle_root(bytes: &[u8]) -> Vec<u8> {
merkleize_padded(&bytes, 0)
}
pub const BYTES_PER_CHUNK: usize = 32;
pub const HASHSIZE: usize = 32;
pub const MERKLE_HASH_CHUNK: usize = 2 * BYTES_PER_CHUNK;
/// Alias to `merkleize_padded(&bytes, minimum_chunk_count)`
///
/// If `minimum_chunk_count < bytes / BYTES_PER_CHUNK`, padding will be added for the difference
/// between the two.
pub fn merkle_root(bytes: &[u8], minimum_chunk_count: usize) -> Vec<u8> {
merkleize_padded(&bytes, minimum_chunk_count)
}
/// Returns the node created by hashing `root` and `length`.
///
/// Used in `TreeHash` for inserting the length of a list above it's root.
pub fn mix_in_length(root: &[u8], length: usize) -> Vec<u8> {
let mut length_bytes = length.to_le_bytes().to_vec();
length_bytes.resize(BYTES_PER_CHUNK, 0);
merkleize_padded::hash_concat(root, &length_bytes)
}
#[derive(Debug, PartialEq, Clone)]
pub enum TreeHashType {
Basic,
@@ -84,3 +97,20 @@ macro_rules! tree_hash_ssz_encoding_as_list {
}
};
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn mix_length() {
let hash = {
let mut preimage = vec![42; BYTES_PER_CHUNK];
preimage.append(&mut vec![42]);
preimage.append(&mut vec![0; BYTES_PER_CHUNK - 1]);
hashing::hash(&preimage)
};
assert_eq!(mix_in_length(&[42; BYTES_PER_CHUNK], 42), hash);
}
}
+1 -1
View File
@@ -243,7 +243,7 @@ fn concat(mut vec1: Vec<u8>, mut vec2: Vec<u8>) -> Vec<u8> {
}
/// Compute the hash of two other hashes concatenated.
fn hash_concat(h1: &[u8], h2: &[u8]) -> Vec<u8> {
pub fn hash_concat(h1: &[u8], h2: &[u8]) -> Vec<u8> {
hash(&concat(h1.to_vec(), h2.to_vec()))
}
+4 -3
View File
@@ -150,7 +150,7 @@ pub fn tree_hash_derive(input: TokenStream) -> TokenStream {
leaves.append(&mut self.#idents.tree_hash_root());
)*
tree_hash::merkle_root(&leaves)
tree_hash::merkle_root(&leaves, 0)
}
}
};
@@ -162,6 +162,7 @@ pub fn tree_hash_signed_root_derive(input: TokenStream) -> TokenStream {
let item = parse_macro_input!(input as DeriveInput);
let name = &item.ident;
let (impl_generics, ty_generics, where_clause) = &item.generics.split_for_impl();
let struct_data = match &item.data {
syn::Data::Struct(s) => s,
@@ -172,7 +173,7 @@ pub fn tree_hash_signed_root_derive(input: TokenStream) -> TokenStream {
let num_elems = idents.len();
let output = quote! {
impl tree_hash::SignedRoot for #name {
impl #impl_generics tree_hash::SignedRoot for #name #ty_generics #where_clause {
fn signed_root(&self) -> Vec<u8> {
let mut leaves = Vec::with_capacity(#num_elems * tree_hash::HASHSIZE);
@@ -180,7 +181,7 @@ pub fn tree_hash_signed_root_derive(input: TokenStream) -> TokenStream {
leaves.append(&mut self.#idents.tree_hash_root());
)*
tree_hash::merkle_root(&leaves)
tree_hash::merkle_root(&leaves, 0)
}
}
};
-179
View File
@@ -1,179 +0,0 @@
use cached_tree_hash::{CachedTreeHash, TreeHashCache};
use tree_hash::{merkle_root, SignedRoot, TreeHash};
use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash};
#[derive(Clone, Debug, TreeHash, CachedTreeHash)]
pub struct Inner {
pub a: u64,
pub b: u64,
pub c: u64,
pub d: u64,
}
fn test_standard_and_cached<T: CachedTreeHash>(original: &T, modified: &T) {
// let mut cache = original.new_tree_hash_cache().unwrap();
let mut cache = TreeHashCache::new(original).unwrap();
let standard_root = original.tree_hash_root();
let cached_root = cache.tree_hash_root().unwrap();
assert_eq!(standard_root, cached_root);
// Test after a modification
cache.update(modified).unwrap();
let standard_root = modified.tree_hash_root();
let cached_root = cache.tree_hash_root().unwrap();
assert_eq!(standard_root, cached_root);
}
#[test]
fn inner_standard_vs_cached() {
let original = Inner {
a: 1,
b: 2,
c: 3,
d: 4,
};
let modified = Inner {
b: 42,
..original.clone()
};
test_standard_and_cached(&original, &modified);
}
#[derive(Clone, Debug, TreeHash, CachedTreeHash)]
pub struct Uneven {
pub a: u64,
pub b: u64,
pub c: u64,
pub d: u64,
pub e: u64,
}
#[test]
fn uneven_standard_vs_cached() {
let original = Uneven {
a: 1,
b: 2,
c: 3,
d: 4,
e: 5,
};
let modified = Uneven {
e: 42,
..original.clone()
};
test_standard_and_cached(&original, &modified);
}
#[derive(Clone, Debug, TreeHash, SignedRoot)]
pub struct SignedInner {
pub a: u64,
pub b: u64,
pub c: u64,
pub d: u64,
#[signed_root(skip_hashing)]
pub e: u64,
}
#[test]
fn signed_root() {
let unsigned = Inner {
a: 1,
b: 2,
c: 3,
d: 4,
};
let signed = SignedInner {
a: 1,
b: 2,
c: 3,
d: 4,
e: 5,
};
assert_eq!(unsigned.tree_hash_root(), signed.signed_root());
}
#[derive(TreeHash, SignedRoot)]
struct CryptoKitties {
best_kitty: u64,
worst_kitty: u8,
kitties: Vec<u32>,
}
impl CryptoKitties {
fn new() -> Self {
CryptoKitties {
best_kitty: 9999,
worst_kitty: 1,
kitties: vec![2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43],
}
}
fn hash(&self) -> Vec<u8> {
let mut leaves = vec![];
leaves.append(&mut self.best_kitty.tree_hash_root());
leaves.append(&mut self.worst_kitty.tree_hash_root());
leaves.append(&mut self.kitties.tree_hash_root());
merkle_root(&leaves)
}
}
#[test]
fn test_simple_tree_hash_derive() {
let kitties = CryptoKitties::new();
assert_eq!(kitties.tree_hash_root(), kitties.hash());
}
#[test]
fn test_simple_signed_root_derive() {
let kitties = CryptoKitties::new();
assert_eq!(kitties.signed_root(), kitties.hash());
}
#[derive(TreeHash, SignedRoot)]
struct Casper {
friendly: bool,
#[tree_hash(skip_hashing)]
friends: Vec<u32>,
#[signed_root(skip_hashing)]
dead: bool,
}
impl Casper {
fn new() -> Self {
Casper {
friendly: true,
friends: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
dead: true,
}
}
fn expected_signed_hash(&self) -> Vec<u8> {
let mut list = Vec::new();
list.append(&mut self.friendly.tree_hash_root());
list.append(&mut self.friends.tree_hash_root());
merkle_root(&list)
}
fn expected_tree_hash(&self) -> Vec<u8> {
let mut list = Vec::new();
list.append(&mut self.friendly.tree_hash_root());
list.append(&mut self.dead.tree_hash_root());
merkle_root(&list)
}
}
#[test]
fn test_annotated_tree_hash_derive() {
let casper = Casper::new();
assert_eq!(casper.tree_hash_root(), casper.expected_tree_hash());
}
#[test]
fn test_annotated_signed_root_derive() {
let casper = Casper::new();
assert_eq!(casper.signed_root(), casper.expected_signed_hash());
}