Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/main' into bitvector
Browse files Browse the repository at this point in the history
michaelsproul committed Sep 3, 2024

Verified

This commit was signed with the committer’s verified signature.
michaelsproul Michael Sproul
2 parents 90804d4 + c28ae8c commit c8a1294
Showing 5 changed files with 237 additions and 53 deletions.
11 changes: 6 additions & 5 deletions ssz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ethereum_ssz"
version = "0.5.4"
version = "0.7.1"
edition = "2021"
description = "SimpleSerialize (SSZ) as used in Ethereum"
license = "Apache-2.0"
@@ -14,11 +14,12 @@ categories = ["cryptography::cryptocurrencies"]
name = "ssz"

[dev-dependencies]
ethereum_ssz_derive = { version = "0.5.4", path = "../ssz_derive" }
alloy-primitives = { version = "0.8.0", features = ["getrandom"] }
ethereum_ssz_derive = { version = "0.7.1", path = "../ssz_derive" }

[dependencies]
ethereum-types = "0.14.1"
ethereum_serde_utils = "0.5.0"
alloy-primitives = "0.8.0"
ethereum_serde_utils = "0.7.0"
smallvec = { version = "1.6.1", features = ["const_generics"] }
itertools = "0.13.0"
serde = "1.0.0"
@@ -28,4 +29,4 @@ derivative = "2.1.1"
arbitrary = { version = "1.0", features = ["derive"], optional = true }

[features]
arbitrary = ["dep:arbitrary", "ethereum-types/arbitrary"]
arbitrary = ["dep:arbitrary", "alloy-primitives/arbitrary"]
57 changes: 47 additions & 10 deletions ssz/src/decode/impls.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::*;
use crate::decode::try_from_iter::{TryCollect, TryFromIter};
use alloy_primitives::{Address, Bloom, Bytes, FixedBytes, U128, U256};
use core::num::NonZeroUsize;
use ethereum_types::{H160, H256, U128, U256};
use itertools::process_results;
use smallvec::SmallVec;
use std::collections::{BTreeMap, BTreeSet};
@@ -275,7 +275,7 @@ impl<T: Decode> Decode for Arc<T> {
}
}

impl Decode for H160 {
impl Decode for Address {
fn is_ssz_fixed_len() -> bool {
true
}
@@ -296,13 +296,37 @@ impl Decode for H160 {
}
}

impl Decode for H256 {
impl<const N: usize> Decode for FixedBytes<N> {
fn is_ssz_fixed_len() -> bool {
true
}

fn ssz_fixed_len() -> usize {
32
N
}

fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
if bytes.len() != N {
return Err(DecodeError::InvalidByteLength {
len: bytes.len(),
expected: N,
});
}

let mut fixed_array = [0u8; N];
fixed_array.copy_from_slice(bytes);

Ok(Self(fixed_array))
}
}

impl Decode for Bloom {
fn is_ssz_fixed_len() -> bool {
true
}

fn ssz_fixed_len() -> usize {
256
}

fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
@@ -312,7 +336,7 @@ impl Decode for H256 {
if len != expected {
Err(DecodeError::InvalidByteLength { len, expected })
} else {
Ok(H256::from_slice(bytes))
Ok(Self::from_slice(bytes))
}
}
}
@@ -333,11 +357,23 @@ impl Decode for U256 {
if len != expected {
Err(DecodeError::InvalidByteLength { len, expected })
} else {
Ok(U256::from_little_endian(bytes))
Ok(U256::from_le_slice(bytes))
}
}
}

impl Decode for Bytes {
#[inline]
fn is_ssz_fixed_len() -> bool {
false
}

#[inline]
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
Ok(bytes.to_vec().into())
}
}

impl Decode for U128 {
fn is_ssz_fixed_len() -> bool {
true
@@ -354,7 +390,7 @@ impl Decode for U128 {
if len != expected {
Err(DecodeError::InvalidByteLength { len, expected })
} else {
Ok(U128::from_little_endian(bytes))
Ok(U128::from_le_slice(bytes))
}
}
}
@@ -527,6 +563,7 @@ pub fn decode_list_of_variable_length_items<T: Decode, Container: TryFromIter<T>
#[cfg(test)]
mod tests {
use super::*;
use alloy_primitives::B256;

// Note: decoding of valid bytes is generally tested "indirectly" in the `/tests` dir, by
// encoding then decoding the element.
@@ -576,17 +613,17 @@ mod tests {
}

#[test]
fn invalid_h256() {
fn invalid_b256() {
assert_eq!(
H256::from_ssz_bytes(&[0; 33]),
B256::from_ssz_bytes(&[0; 33]),
Err(DecodeError::InvalidByteLength {
len: 33,
expected: 32
})
);

assert_eq!(
H256::from_ssz_bytes(&[0; 31]),
B256::from_ssz_bytes(&[0; 31]),
Err(DecodeError::InvalidByteLength {
len: 31,
expected: 32
93 changes: 72 additions & 21 deletions ssz/src/encode/impls.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::*;
use alloy_primitives::{Address, Bloom, Bytes, FixedBytes, U128, U256};
use core::num::NonZeroUsize;
use ethereum_types::{H160, H256, U128, U256};
use smallvec::SmallVec;
use std::collections::{BTreeMap, BTreeSet};
use std::sync::Arc;
@@ -409,7 +409,7 @@ impl Encode for NonZeroUsize {
}
}

impl Encode for H160 {
impl Encode for Address {
fn is_ssz_fixed_len() -> bool {
true
}
@@ -423,25 +423,83 @@ impl Encode for H160 {
}

fn ssz_append(&self, buf: &mut Vec<u8>) {
buf.extend_from_slice(self.as_bytes());
buf.extend_from_slice(self.as_slice());
}
}

impl Encode for H256 {
impl<const N: usize> Encode for FixedBytes<N> {
#[inline]
fn is_ssz_fixed_len() -> bool {
true
}

#[inline]
fn ssz_bytes_len(&self) -> usize {
N
}

#[inline]
fn ssz_fixed_len() -> usize {
32
N
}

#[inline]
fn ssz_append(&self, buf: &mut Vec<u8>) {
buf.extend_from_slice(&self.0);
}

#[inline]
fn as_ssz_bytes(&self) -> Vec<u8> {
self.0.to_vec()
}
}

impl Encode for Bloom {
#[inline]
fn is_ssz_fixed_len() -> bool {
true
}

#[inline]
fn ssz_bytes_len(&self) -> usize {
32
256
}

#[inline]
fn ssz_fixed_len() -> usize {
256
}

#[inline]
fn ssz_append(&self, buf: &mut Vec<u8>) {
buf.extend_from_slice(self.as_bytes());
buf.extend_from_slice(&self.0 .0);
}

#[inline]
fn as_ssz_bytes(&self) -> Vec<u8> {
self.0.to_vec()
}
}

impl Encode for Bytes {
#[inline]
fn is_ssz_fixed_len() -> bool {
false
}

#[inline]
fn ssz_bytes_len(&self) -> usize {
self.0.len()
}

#[inline]
fn ssz_append(&self, buf: &mut Vec<u8>) {
buf.extend_from_slice(&self.0);
}

#[inline]
fn as_ssz_bytes(&self) -> Vec<u8> {
self.0.to_vec()
}
}

@@ -459,11 +517,7 @@ impl Encode for U256 {
}

fn ssz_append(&self, buf: &mut Vec<u8>) {
let n = <Self as Encode>::ssz_fixed_len();
let s = buf.len();

buf.resize(s + n, 0);
self.to_little_endian(&mut buf[s..]);
buf.extend_from_slice(self.as_le_slice());
}
}

@@ -481,11 +535,7 @@ impl Encode for U128 {
}

fn ssz_append(&self, buf: &mut Vec<u8>) {
let n = <Self as Encode>::ssz_fixed_len();
let s = buf.len();

buf.resize(s + n, 0);
self.to_little_endian(&mut buf[s..]);
buf.extend_from_slice(self.as_le_slice());
}
}

@@ -518,6 +568,7 @@ impl_encodable_for_u8_array!(48);
#[cfg(test)]
mod tests {
use super::*;
use alloy_primitives::B256;

#[test]
fn vec_of_u8() {
@@ -619,16 +670,16 @@ mod tests {
}

#[test]
fn ssz_encode_h256() {
assert_eq!(H256::from(&[0; 32]).as_ssz_bytes(), vec![0; 32]);
assert_eq!(H256::from(&[1; 32]).as_ssz_bytes(), vec![1; 32]);
fn ssz_encode_b256() {
assert_eq!(B256::from(&[0; 32]).as_ssz_bytes(), vec![0; 32]);
assert_eq!(B256::from(&[1; 32]).as_ssz_bytes(), vec![1; 32]);

let bytes = vec![
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,
];

assert_eq!(H256::from_slice(&bytes).as_ssz_bytes(), bytes);
assert_eq!(B256::from_slice(&bytes).as_ssz_bytes(), bytes);
}

#[test]
127 changes: 111 additions & 16 deletions ssz/tests/tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use ethereum_types::{H160, H256};
use alloy_primitives::{Address, Bloom, Bytes, B256, U128, U256};
use ssz::{Decode, DecodeError, Encode};
use ssz_derive::{Decode, Encode};
use std::num::NonZeroUsize;
@@ -48,45 +48,53 @@ mod round_trip {
}

#[test]
fn h160() {
let items: Vec<H160> = vec![H160::zero(), H160::from([1; 20]), H160::random()];
fn address() {
let items: Vec<Address> = vec![
Address::repeat_byte(0),
Address::from([1; 20]),
Address::random(),
];

round_trip(items);
}

#[test]
fn vec_of_h160() {
let items: Vec<Vec<H160>> = vec![
fn vec_of_address() {
let items: Vec<Vec<Address>> = vec![
vec![],
vec![H160::zero(), H160::from([1; 20]), H160::random()],
vec![
Address::repeat_byte(0),
Address::from([1; 20]),
Address::random(),
],
];

round_trip(items);
}

#[test]
fn h256() {
let items: Vec<H256> = vec![H256::zero(), H256::from([1; 32]), H256::random()];
let items: Vec<B256> = vec![B256::repeat_byte(0), B256::from([1; 32]), B256::random()];

round_trip(items);
}

#[test]
fn vec_of_h256() {
let items: Vec<Vec<H256>> = vec![
fn vec_of_b256() {
let items: Vec<Vec<B256>> = vec![
vec![],
vec![H256::zero(), H256::from([1; 32]), H256::random()],
vec![B256::ZERO, B256::from([1; 32]), B256::random()],
];

round_trip(items);
}

#[test]
fn option_vec_h256() {
let items: Vec<Option<Vec<H256>>> = vec![
fn option_vec_b256() {
let items: Vec<Option<Vec<B256>>> = vec![
None,
Some(vec![]),
Some(vec![H256::zero(), H256::from([1; 32]), H256::random()]),
Some(vec![B256::ZERO, B256::from([1; 32]), B256::random()]),
];

round_trip(items);
@@ -471,6 +479,87 @@ mod round_trip {
let data = vec![Arc::new(vec![0u64]), Arc::new(vec![u64::MAX; 10])];
round_trip(data);
}

#[test]
fn alloy_u128() {
let data = vec![
U128::from(0),
U128::from(u128::MAX),
U128::from(u64::MAX),
U128::from(255),
];
round_trip(data);
}

#[test]
fn vec_of_option_alloy_u128() {
let data = vec![
vec![Some(U128::from(u128::MAX)), Some(U128::from(0)), None],
vec![None],
vec![],
vec![Some(U128::from(0))],
];
round_trip(data);
}

#[test]
fn u256() {
let data = vec![
U256::from(0),
U256::MAX,
U256::from(u64::MAX),
U256::from(255),
];
round_trip(data);
}

#[test]
fn vec_of_option_u256() {
let data = vec![
vec![Some(U256::MAX), Some(U256::from(0)), None],
vec![None],
vec![],
vec![Some(U256::from(0))],
];
round_trip(data);
}

#[test]
fn alloy_bytes() {
let data = vec![
Bytes::new(),
Bytes::from_static(&[1, 2, 3]),
Bytes::from_static(&[0; 32]),
Bytes::from_static(&[0]),
];
round_trip(data);
}

#[test]
fn tuple_option() {
let data = vec![(48u8, Some(0u64)), (0u8, None), (u8::MAX, Some(u64::MAX))];
round_trip(data);
}

#[test]
fn bloom() {
let data = vec![
Bloom::ZERO,
Bloom::with_last_byte(5),
Bloom::repeat_byte(73),
];
round_trip(data);
}

#[test]
fn vec_bloom() {
let data = vec![
vec![Bloom::ZERO, Bloom::ZERO, Bloom::with_last_byte(5)],
vec![],
vec![Bloom::repeat_byte(73), Bloom::repeat_byte(72)],
];
round_trip(data);
}
}

/// Decode tests that are expected to fail.
@@ -485,13 +574,19 @@ mod decode_fail {

#[test]
fn hash160() {
let long_bytes = H256::repeat_byte(0xff).as_ssz_bytes();
assert!(H160::from_ssz_bytes(&long_bytes).is_err());
let long_bytes = B256::repeat_byte(0xff).as_ssz_bytes();
assert!(Address::from_ssz_bytes(&long_bytes).is_err());
}

#[test]
fn hash256() {
let long_bytes = vec![0xff; 33];
assert!(B256::from_ssz_bytes(&long_bytes).is_err());
}

#[test]
fn bloom() {
let long_bytes = vec![0xff; 257];
assert!(H256::from_ssz_bytes(&long_bytes).is_err());
assert!(Bloom::from_ssz_bytes(&long_bytes).is_err());
}
}
2 changes: 1 addition & 1 deletion ssz_derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ethereum_ssz_derive"
version = "0.5.4"
version = "0.7.1"
edition = "2021"
description = "Procedural derive macros to accompany the ethereum_ssz crate"
license = "Apache-2.0"

0 comments on commit c8a1294

Please sign in to comment.