Merge latest master

This commit is contained in:
Age Manning
2020-04-22 01:05:46 +10:00
73 changed files with 2631 additions and 612 deletions

View File

@@ -19,9 +19,7 @@ impl AggregatePublicKey {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
let pubkey = RawAggregatePublicKey::from_bytes(&bytes).map_err(|_| {
DecodeError::BytesInvalid(
format!("Invalid AggregatePublicKey bytes: {:?}", bytes).to_string(),
)
DecodeError::BytesInvalid(format!("Invalid AggregatePublicKey bytes: {:?}", bytes))
})?;
Ok(AggregatePublicKey(pubkey))

View File

@@ -164,8 +164,8 @@ impl TreeHashCache {
fn lift_dirty(dirty_indices: &[usize]) -> SmallVec8<usize> {
let mut new_dirty = SmallVec8::with_capacity(dirty_indices.len());
for i in 0..dirty_indices.len() {
new_dirty.push(dirty_indices[i] / 2)
for index in dirty_indices {
new_dirty.push(index / 2)
}
new_dirty.dedup();

View File

@@ -89,6 +89,7 @@ impl<T: Encode + Decode> CacheArena<T> {
/// To reiterate, the given `range` should be relative to the given `alloc_id`, not
/// `self.backing`. E.g., if the allocation has an offset of `20` and the range is `0..1`, then
/// the splice will translate to `self.backing[20..21]`.
#[allow(clippy::comparison_chain)]
fn splice_forgetful<I: IntoIterator<Item = T>>(
&mut self,
alloc_id: usize,

View File

@@ -8,6 +8,7 @@ edition = "2018"
ethereum-types = "0.8.0"
eth2_hashing = "0.1.0"
lazy_static = "1.4.0"
safe_arith = { path = "../safe_arith" }
[dev-dependencies]
quickcheck = "0.9.0"

View File

@@ -1,6 +1,7 @@
use eth2_hashing::{hash, hash32_concat, ZERO_HASHES};
use ethereum_types::H256;
use lazy_static::lazy_static;
use safe_arith::ArithError;
const MAX_TREE_DEPTH: usize = 32;
const EMPTY_SLICE: &[H256] = &[];
@@ -38,6 +39,8 @@ pub enum MerkleTreeError {
Invalid,
// Incorrect Depth provided
DepthTooSmall,
// Overflow occurred
ArithError,
}
impl MerkleTree {
@@ -232,6 +235,12 @@ fn merkle_root_from_branch(leaf: H256, branch: &[H256], depth: usize, index: usi
H256::from_slice(&merkle_root)
}
impl From<ArithError> for MerkleTreeError {
fn from(_: ArithError) -> Self {
MerkleTreeError::ArithError
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -0,0 +1,9 @@
[package]
name = "safe_arith"
version = "0.1.0"
authors = ["Michael Sproul <michael@sigmaprime.io>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@@ -0,0 +1,161 @@
//! Library for safe arithmetic on integers, avoiding overflow and division by zero.
/// Error representing the failure of an arithmetic operation.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum ArithError {
Overflow,
DivisionByZero,
}
type Result<T> = std::result::Result<T, ArithError>;
macro_rules! assign_method {
($name:ident, $op:ident, $doc_op:expr) => {
assign_method!($name, $op, Self, $doc_op);
};
($name:ident, $op:ident, $rhs_ty:ty, $doc_op:expr) => {
#[doc = "Safe variant of `"]
#[doc = $doc_op]
#[doc = "`."]
fn $name(&mut self, other: $rhs_ty) -> Result<()> {
*self = self.$op(other)?;
Ok(())
}
};
}
/// Trait providing safe arithmetic operations for built-in types.
pub trait SafeArith: Sized + Copy {
const ZERO: Self;
const ONE: Self;
/// Safe variant of `+` that guards against overflow.
fn safe_add(&self, other: Self) -> Result<Self>;
/// Safe variant of `-` that guards against overflow.
fn safe_sub(&self, other: Self) -> Result<Self>;
/// Safe variant of `*` that guards against overflow.
fn safe_mul(&self, other: Self) -> Result<Self>;
/// Safe variant of `/` that guards against division by 0.
fn safe_div(&self, other: Self) -> Result<Self>;
/// Safe variant of `%` that guards against division by 0.
fn safe_rem(&self, other: Self) -> Result<Self>;
/// Safe variant of `<<` that guards against overflow.
fn safe_shl(&self, other: u32) -> Result<Self>;
/// Safe variant of `>>` that guards against overflow.
fn safe_shr(&self, other: u32) -> Result<Self>;
assign_method!(safe_add_assign, safe_add, "+=");
assign_method!(safe_sub_assign, safe_sub, "-=");
assign_method!(safe_mul_assign, safe_mul, "*=");
assign_method!(safe_div_assign, safe_div, "/=");
assign_method!(safe_rem_assign, safe_rem, "%=");
assign_method!(safe_shl_assign, safe_shl, u32, "<<=");
assign_method!(safe_shr_assign, safe_shr, u32, ">>=");
/// Mutate `self` by adding 1, erroring on overflow.
fn increment(&mut self) -> Result<()> {
self.safe_add_assign(Self::ONE)
}
}
macro_rules! impl_safe_arith {
($typ:ty) => {
impl SafeArith for $typ {
const ZERO: Self = 0;
const ONE: Self = 1;
fn safe_add(&self, other: Self) -> Result<Self> {
self.checked_add(other).ok_or(ArithError::Overflow)
}
fn safe_sub(&self, other: Self) -> Result<Self> {
self.checked_sub(other).ok_or(ArithError::Overflow)
}
fn safe_mul(&self, other: Self) -> Result<Self> {
self.checked_mul(other).ok_or(ArithError::Overflow)
}
fn safe_div(&self, other: Self) -> Result<Self> {
self.checked_div(other).ok_or(ArithError::DivisionByZero)
}
fn safe_rem(&self, other: Self) -> Result<Self> {
self.checked_rem(other).ok_or(ArithError::DivisionByZero)
}
fn safe_shl(&self, other: u32) -> Result<Self> {
self.checked_shl(other).ok_or(ArithError::Overflow)
}
fn safe_shr(&self, other: u32) -> Result<Self> {
self.checked_shr(other).ok_or(ArithError::Overflow)
}
}
};
}
impl_safe_arith!(u8);
impl_safe_arith!(u16);
impl_safe_arith!(u32);
impl_safe_arith!(u64);
impl_safe_arith!(usize);
impl_safe_arith!(i8);
impl_safe_arith!(i16);
impl_safe_arith!(i32);
impl_safe_arith!(i64);
impl_safe_arith!(isize);
#[cfg(test)]
mod test {
use super::*;
#[test]
fn basic() {
let x = 10u32;
let y = 11;
assert_eq!(x.safe_add(y), Ok(x + y));
assert_eq!(y.safe_sub(x), Ok(y - x));
assert_eq!(x.safe_mul(y), Ok(x * y));
assert_eq!(x.safe_div(y), Ok(x / y));
assert_eq!(x.safe_rem(y), Ok(x % y));
assert_eq!(x.safe_shl(1), Ok(x << 1));
assert_eq!(x.safe_shr(1), Ok(x >> 1));
}
#[test]
fn mutate() {
let mut x = 0u8;
x.increment().unwrap();
x.increment().unwrap();
assert_eq!(x, 2);
x.safe_sub_assign(1).unwrap();
assert_eq!(x, 1);
x.safe_shl_assign(1).unwrap();
assert_eq!(x, 2);
x.safe_mul_assign(3).unwrap();
assert_eq!(x, 6);
x.safe_div_assign(4).unwrap();
assert_eq!(x, 1);
x.safe_shr_assign(1).unwrap();
assert_eq!(x, 0);
}
#[test]
fn errors() {
assert!(u32::max_value().safe_add(1).is_err());
assert!(u32::min_value().safe_sub(1).is_err());
assert!(u32::max_value().safe_mul(2).is_err());
assert!(u32::max_value().safe_div(0).is_err());
assert!(u32::max_value().safe_rem(0).is_err());
assert!(u32::max_value().safe_shl(32).is_err());
assert!(u32::max_value().safe_shr(32).is_err());
}
}

View File

@@ -1,4 +1,3 @@
use hex;
use hex::ToHex;
use serde::de::{self, Visitor};
use std::fmt;

View File

@@ -21,10 +21,72 @@ pub enum DecodeError {
/// length items (i.e., `length[0] < BYTES_PER_LENGTH_OFFSET`).
/// - When decoding variable-length items, the `n`'th offset was less than the `n-1`'th offset.
OutOfBoundsByte { i: usize },
/// An offset points “backwards” into the fixed-bytes portion of the message, essentially
/// double-decoding bytes that will also be decoded as fixed-length.
///
/// https://notes.ethereum.org/ruKvDXl6QOW3gnqVYb8ezA?view#1-Offset-into-fixed-portion
OffsetIntoFixedPortion(usize),
/// The first offset does not point to the byte that follows the fixed byte portion,
/// essentially skipping a variable-length byte.
///
/// https://notes.ethereum.org/ruKvDXl6QOW3gnqVYb8ezA?view#2-Skip-first-variable-byte
OffsetSkipsVariableBytes(usize),
/// An offset points to bytes prior to the previous offset. Depending on how you look at it,
/// this either double-decodes bytes or makes the first offset a negative-length.
///
/// https://notes.ethereum.org/ruKvDXl6QOW3gnqVYb8ezA?view#3-Offsets-are-decreasing
OffsetsAreDecreasing(usize),
/// An offset references byte indices that do not exist in the source bytes.
///
/// https://notes.ethereum.org/ruKvDXl6QOW3gnqVYb8ezA?view#4-Offsets-are-out-of-bounds
OffsetOutOfBounds(usize),
/// A variable-length list does not have a fixed portion that is cleanly divisible by
/// `BYTES_PER_LENGTH_OFFSET`.
InvalidListFixedBytesLen(usize),
/// Some item has a `ssz_fixed_len` of zero. This is illegal.
ZeroLengthItem,
/// The given bytes were invalid for some application-level reason.
BytesInvalid(String),
}
/// Performs checks on the `offset` based upon the other parameters provided.
///
/// ## Detail
///
/// - `offset`: the offset bytes (e.g., result of `read_offset(..)`).
/// - `previous_offset`: unless this is the first offset in the SSZ object, the value of the
/// previously-read offset. Used to ensure offsets are not decreasing.
/// - `num_bytes`: the total number of bytes in the SSZ object. Used to ensure the offset is not
/// out of bounds.
/// - `num_fixed_bytes`: the number of fixed-bytes in the struct, if it is known. Used to ensure
/// that the first offset doesn't skip any variable bytes.
///
/// ## References
///
/// The checks here are derived from this document:
///
/// https://notes.ethereum.org/ruKvDXl6QOW3gnqVYb8ezA?view
pub fn sanitize_offset(
offset: usize,
previous_offset: Option<usize>,
num_bytes: usize,
num_fixed_bytes: Option<usize>,
) -> Result<usize, DecodeError> {
if num_fixed_bytes.map_or(false, |fixed_bytes| offset < fixed_bytes) {
Err(DecodeError::OffsetIntoFixedPortion(offset))
} else if previous_offset.is_none()
&& num_fixed_bytes.map_or(false, |fixed_bytes| offset != fixed_bytes)
{
Err(DecodeError::OffsetSkipsVariableBytes(offset))
} else if offset > num_bytes {
Err(DecodeError::OffsetOutOfBounds(offset))
} else if previous_offset.map_or(false, |prev| prev > offset) {
Err(DecodeError::OffsetsAreDecreasing(offset))
} else {
Ok(offset)
}
}
/// Provides SSZ decoding (de-serialization) via the `from_ssz_bytes(&bytes)` method.
///
/// See `examples/` for manual implementations or the crate root for implementations using
@@ -97,21 +159,14 @@ impl<'a> SszDecoderBuilder<'a> {
self.items.push(slice);
} else {
let offset = read_offset(&self.bytes[self.items_index..])?;
let previous_offset = self
.offsets
.last()
.map(|o| o.offset)
.unwrap_or_else(|| BYTES_PER_LENGTH_OFFSET);
if (previous_offset > offset) || (offset > self.bytes.len()) {
return Err(DecodeError::OutOfBoundsByte { i: offset });
}
self.offsets.push(Offset {
position: self.items.len(),
offset,
offset: sanitize_offset(
read_offset(&self.bytes[self.items_index..])?,
self.offsets.last().map(|o| o.offset),
self.bytes.len(),
None,
)?,
});
// Push an empty slice into items; it will be replaced later.
@@ -124,13 +179,13 @@ impl<'a> SszDecoderBuilder<'a> {
}
fn finalize(&mut self) -> Result<(), DecodeError> {
if !self.offsets.is_empty() {
if let Some(first_offset) = self.offsets.first().map(|o| o.offset) {
// Check to ensure the first offset points to the byte immediately following the
// fixed-length bytes.
if self.offsets[0].offset != self.items_index {
return Err(DecodeError::OutOfBoundsByte {
i: self.offsets[0].offset,
});
if first_offset < self.items_index {
return Err(DecodeError::OffsetIntoFixedPortion(first_offset));
} else if first_offset > self.items_index {
return Err(DecodeError::OffsetSkipsVariableBytes(first_offset));
}
// Iterate through each pair of offsets, grabbing the slice between each of the offsets.

View File

@@ -366,7 +366,7 @@ impl_decodable_for_u8_array!(4);
impl_decodable_for_u8_array!(32);
macro_rules! impl_for_vec {
($type: ty) => {
($type: ty, $max_len: expr) => {
impl<T: Decode> Decode for $type {
fn is_ssz_fixed_len() -> bool {
false
@@ -381,22 +381,22 @@ macro_rules! impl_for_vec {
.map(|chunk| T::from_ssz_bytes(chunk))
.collect()
} else {
decode_list_of_variable_length_items(bytes).map(|vec| vec.into())
decode_list_of_variable_length_items(bytes, $max_len).map(|vec| vec.into())
}
}
}
};
}
impl_for_vec!(Vec<T>);
impl_for_vec!(SmallVec<[T; 1]>);
impl_for_vec!(SmallVec<[T; 2]>);
impl_for_vec!(SmallVec<[T; 3]>);
impl_for_vec!(SmallVec<[T; 4]>);
impl_for_vec!(SmallVec<[T; 5]>);
impl_for_vec!(SmallVec<[T; 6]>);
impl_for_vec!(SmallVec<[T; 7]>);
impl_for_vec!(SmallVec<[T; 8]>);
impl_for_vec!(Vec<T>, None);
impl_for_vec!(SmallVec<[T; 1]>, Some(1));
impl_for_vec!(SmallVec<[T; 2]>, Some(2));
impl_for_vec!(SmallVec<[T; 3]>, Some(3));
impl_for_vec!(SmallVec<[T; 4]>, Some(4));
impl_for_vec!(SmallVec<[T; 5]>, Some(5));
impl_for_vec!(SmallVec<[T; 6]>, Some(6));
impl_for_vec!(SmallVec<[T; 7]>, Some(7));
impl_for_vec!(SmallVec<[T; 8]>, Some(8));
/// Decodes `bytes` as if it were a list of variable-length items.
///
@@ -405,43 +405,52 @@ impl_for_vec!(SmallVec<[T; 8]>);
/// differing types.
pub fn decode_list_of_variable_length_items<T: Decode>(
bytes: &[u8],
max_len: Option<usize>,
) -> Result<Vec<T>, DecodeError> {
let mut next_variable_byte = read_offset(bytes)?;
// The value of the first offset must not point back into the same bytes that defined
// it.
if next_variable_byte < BYTES_PER_LENGTH_OFFSET {
return Err(DecodeError::OutOfBoundsByte {
i: next_variable_byte,
});
if bytes.is_empty() {
return Ok(vec![]);
}
let num_items = next_variable_byte / BYTES_PER_LENGTH_OFFSET;
let first_offset = read_offset(bytes)?;
sanitize_offset(first_offset, None, bytes.len(), Some(first_offset))?;
// The fixed-length section must be a clean multiple of `BYTES_PER_LENGTH_OFFSET`.
if next_variable_byte != num_items * BYTES_PER_LENGTH_OFFSET {
return Err(DecodeError::InvalidByteLength {
len: next_variable_byte,
expected: num_items * BYTES_PER_LENGTH_OFFSET,
});
if first_offset % BYTES_PER_LENGTH_OFFSET != 0 || first_offset < BYTES_PER_LENGTH_OFFSET {
return Err(DecodeError::InvalidListFixedBytesLen(first_offset));
}
let mut values = Vec::with_capacity(num_items);
let num_items = first_offset / BYTES_PER_LENGTH_OFFSET;
if max_len.map_or(false, |max| num_items > max) {
return Err(DecodeError::BytesInvalid(format!(
"Variable length list of {} items exceeds maximum of {:?}",
num_items, max_len
)));
}
// Only initialize the vec with a capacity if a maximum length is provided.
//
// We assume that if a max length is provided then the application is able to handle an
// allocation of this size.
let mut values = if max_len.is_some() {
Vec::with_capacity(num_items)
} else {
vec![]
};
let mut offset = first_offset;
for i in 1..=num_items {
let slice_option = if i == num_items {
bytes.get(next_variable_byte..)
bytes.get(offset..)
} else {
let offset = read_offset(&bytes[(i * BYTES_PER_LENGTH_OFFSET)..])?;
let start = offset;
let start = next_variable_byte;
next_variable_byte = offset;
let next_offset = read_offset(&bytes[(i * BYTES_PER_LENGTH_OFFSET)..])?;
offset = sanitize_offset(next_offset, Some(offset), bytes.len(), Some(first_offset))?;
bytes.get(start..next_variable_byte)
bytes.get(start..offset)
};
let slice = slice_option.ok_or_else(|| DecodeError::OutOfBoundsByte {
i: next_variable_byte,
})?;
let slice = slice_option.ok_or_else(|| DecodeError::OutOfBoundsByte { i: offset })?;
values.push(T::from_ssz_bytes(slice)?);
}
@@ -519,26 +528,34 @@ mod tests {
);
}
#[test]
fn empty_list() {
let vec: Vec<Vec<u16>> = vec![];
let bytes = vec.as_ssz_bytes();
assert!(bytes.is_empty());
assert_eq!(Vec::from_ssz_bytes(&bytes), Ok(vec),);
}
#[test]
fn first_length_points_backwards() {
assert_eq!(
<Vec<Vec<u16>>>::from_ssz_bytes(&[0, 0, 0, 0]),
Err(DecodeError::OutOfBoundsByte { i: 0 })
Err(DecodeError::InvalidListFixedBytesLen(0))
);
assert_eq!(
<Vec<Vec<u16>>>::from_ssz_bytes(&[1, 0, 0, 0]),
Err(DecodeError::OutOfBoundsByte { i: 1 })
Err(DecodeError::InvalidListFixedBytesLen(1))
);
assert_eq!(
<Vec<Vec<u16>>>::from_ssz_bytes(&[2, 0, 0, 0]),
Err(DecodeError::OutOfBoundsByte { i: 2 })
Err(DecodeError::InvalidListFixedBytesLen(2))
);
assert_eq!(
<Vec<Vec<u16>>>::from_ssz_bytes(&[3, 0, 0, 0]),
Err(DecodeError::OutOfBoundsByte { i: 3 })
Err(DecodeError::InvalidListFixedBytesLen(3))
);
}
@@ -546,7 +563,7 @@ mod tests {
fn lengths_are_decreasing() {
assert_eq!(
<Vec<Vec<u16>>>::from_ssz_bytes(&[12, 0, 0, 0, 14, 0, 0, 0, 12, 0, 0, 0, 1, 0, 1, 0]),
Err(DecodeError::OutOfBoundsByte { i: 12 })
Err(DecodeError::OffsetsAreDecreasing(12))
);
}
@@ -554,10 +571,7 @@ mod tests {
fn awkward_fixed_length_portion() {
assert_eq!(
<Vec<Vec<u16>>>::from_ssz_bytes(&[10, 0, 0, 0, 10, 0, 0, 0, 0, 0]),
Err(DecodeError::InvalidByteLength {
len: 10,
expected: 8
})
Err(DecodeError::InvalidListFixedBytesLen(10))
);
}
@@ -565,14 +579,15 @@ mod tests {
fn length_out_of_bounds() {
assert_eq!(
<Vec<Vec<u16>>>::from_ssz_bytes(&[5, 0, 0, 0]),
Err(DecodeError::InvalidByteLength {
len: 5,
expected: 4
})
Err(DecodeError::OffsetOutOfBounds(5))
);
assert_eq!(
<Vec<Vec<u16>>>::from_ssz_bytes(&[8, 0, 0, 0, 9, 0, 0, 0]),
Err(DecodeError::OutOfBoundsByte { i: 9 })
Err(DecodeError::OffsetOutOfBounds(9))
);
assert_eq!(
<Vec<Vec<u16>>>::from_ssz_bytes(&[8, 0, 0, 0, 16, 0, 0, 0]),
Err(DecodeError::OffsetOutOfBounds(16))
);
}

View File

@@ -152,7 +152,7 @@ mod round_trip {
assert_eq!(
VariableLen::from_ssz_bytes(&bytes),
Err(DecodeError::OutOfBoundsByte { i: 9 })
Err(DecodeError::OffsetIntoFixedPortion(9))
);
}
@@ -182,7 +182,7 @@ mod round_trip {
assert_eq!(
VariableLen::from_ssz_bytes(&bytes),
Err(DecodeError::OutOfBoundsByte { i: 11 })
Err(DecodeError::OffsetSkipsVariableBytes(11))
);
}
@@ -284,7 +284,7 @@ mod round_trip {
assert_eq!(
ThreeVariableLen::from_ssz_bytes(&bytes),
Err(DecodeError::OutOfBoundsByte { i: 14 })
Err(DecodeError::OffsetsAreDecreasing(14))
);
}

View File

@@ -86,6 +86,7 @@ pub fn ssz_encode_derive(input: TokenStream) -> TokenStream {
let field_types_f = field_types_a.clone();
let output = quote! {
#[allow(clippy::integer_arithmetic)]
impl #impl_generics ssz::Encode for #name #ty_generics #where_clause {
fn is_ssz_fixed_len() -> bool {
#(
@@ -221,6 +222,7 @@ pub fn ssz_decode_derive(input: TokenStream) -> TokenStream {
}
let output = quote! {
#[allow(clippy::integer_arithmetic)]
impl #impl_generics ssz::Decode for #name #ty_generics #where_clause {
fn is_ssz_fixed_len() -> bool {
#(

View File

@@ -224,29 +224,44 @@ where
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
let fixed_len = N::to_usize();
if bytes.is_empty() {
Err(ssz::DecodeError::InvalidByteLength {
len: 0,
expected: 1,
})
} else if T::is_ssz_fixed_len() {
let num_items = bytes
.len()
.checked_div(T::ssz_fixed_len())
.ok_or_else(|| ssz::DecodeError::ZeroLengthItem)?;
if num_items != fixed_len {
return Err(ssz::DecodeError::BytesInvalid(format!(
"FixedVector of {} items has {} items",
num_items, fixed_len
)));
}
bytes
.chunks(T::ssz_fixed_len())
.map(|chunk| T::from_ssz_bytes(chunk))
.collect::<Result<Vec<T>, _>>()
.and_then(|vec| {
if vec.len() == N::to_usize() {
if vec.len() == fixed_len {
Ok(vec.into())
} else {
Err(ssz::DecodeError::BytesInvalid(format!(
"wrong number of vec elements, got: {}, expected: {}",
"Wrong number of FixedVector elements, got: {}, expected: {}",
vec.len(),
N::to_usize()
)))
}
})
} else {
ssz::decode_list_of_variable_length_items(bytes).and_then(|vec| Ok(vec.into()))
ssz::decode_list_of_variable_length_items(bytes, Some(fixed_len))
.and_then(|vec| Ok(vec.into()))
}
}
}

View File

@@ -218,22 +218,41 @@ where
}
}
impl<T, N: Unsigned> ssz::Decode for VariableList<T, N>
impl<T, N> ssz::Decode for VariableList<T, N>
where
T: ssz::Decode,
N: Unsigned,
{
fn is_ssz_fixed_len() -> bool {
<Vec<T>>::is_ssz_fixed_len()
}
fn ssz_fixed_len() -> usize {
<Vec<T>>::ssz_fixed_len()
false
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
let vec = <Vec<T>>::from_ssz_bytes(bytes)?;
let max_len = N::to_usize();
Self::new(vec).map_err(|e| ssz::DecodeError::BytesInvalid(format!("VariableList {:?}", e)))
if bytes.is_empty() {
Ok(vec![].into())
} else if T::is_ssz_fixed_len() {
let num_items = bytes
.len()
.checked_div(T::ssz_fixed_len())
.ok_or_else(|| ssz::DecodeError::ZeroLengthItem)?;
if num_items > max_len {
return Err(ssz::DecodeError::BytesInvalid(format!(
"VariableList of {} items exceeds maximum of {}",
num_items, max_len
)));
}
bytes
.chunks(T::ssz_fixed_len())
.map(|chunk| T::from_ssz_bytes(chunk))
.collect::<Result<Vec<_>, _>>()
.map(Into::into)
} else {
ssz::decode_list_of_variable_length_items(bytes, Some(max_len)).map(|vec| vec.into())
}
}
}

View File

@@ -274,22 +274,20 @@ impl MerkleHasher {
loop {
if let Some(root) = self.root {
break Ok(root);
} else if let Some(node) = self.half_nodes.last() {
let right_child = node.id * 2 + 1;
self.process_right_node(right_child, self.zero_hash(right_child));
} else if self.next_leaf == 1 {
// The next_leaf can only be 1 if the tree has a depth of one. If have been no
// leaves supplied, assume a root of zero.
break Ok(Hash256::zero());
} else {
if let Some(node) = self.half_nodes.last() {
let right_child = node.id * 2 + 1;
self.process_right_node(right_child, self.zero_hash(right_child));
} else if self.next_leaf == 1 {
// The next_leaf can only be 1 if the tree has a depth of one. If have been no
// leaves supplied, assume a root of zero.
break Ok(Hash256::zero());
} else {
// The only scenario where there are (a) no half nodes and (b) a tree of depth
// two or more is where no leaves have been supplied at all.
//
// Once we supply this first zero-hash leaf then all future operations will be
// triggered via the `process_right_node` branch.
self.process_left_node(self.next_leaf, self.zero_hash(self.next_leaf))
}
// The only scenario where there are (a) no half nodes and (b) a tree of depth
// two or more is where no leaves have been supplied at all.
//
// Once we supply this first zero-hash leaf then all future operations will be
// triggered via the `process_right_node` branch.
self.process_left_node(self.next_leaf, self.zero_hash(self.next_leaf))
}
}
}