diff --git a/ssz/Cargo.toml b/ssz/Cargo.toml index cda946e..f65101b 100644 --- a/ssz/Cargo.toml +++ b/ssz/Cargo.toml @@ -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"] diff --git a/ssz/src/decode/impls.rs b/ssz/src/decode/impls.rs index 3317bd2..00c547f 100644 --- a/ssz/src/decode/impls.rs +++ b/ssz/src/decode/impls.rs @@ -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 Decode for Arc { } } -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 Decode for FixedBytes { fn is_ssz_fixed_len() -> bool { true } fn ssz_fixed_len() -> usize { - 32 + N + } + + fn from_ssz_bytes(bytes: &[u8]) -> Result { + 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 { @@ -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 { + 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 #[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,9 +613,9 @@ 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 @@ -586,7 +623,7 @@ mod tests { ); assert_eq!( - H256::from_ssz_bytes(&[0; 31]), + B256::from_ssz_bytes(&[0; 31]), Err(DecodeError::InvalidByteLength { len: 31, expected: 32 diff --git a/ssz/src/encode/impls.rs b/ssz/src/encode/impls.rs index b884e5b..c1c74d9 100644 --- a/ssz/src/encode/impls.rs +++ b/ssz/src/encode/impls.rs @@ -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) { - buf.extend_from_slice(self.as_bytes()); + buf.extend_from_slice(self.as_slice()); } } -impl Encode for H256 { +impl Encode for FixedBytes { + #[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) { + buf.extend_from_slice(&self.0); + } + + #[inline] + fn as_ssz_bytes(&self) -> Vec { + 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) { - buf.extend_from_slice(self.as_bytes()); + buf.extend_from_slice(&self.0 .0); + } + + #[inline] + fn as_ssz_bytes(&self) -> Vec { + 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) { + buf.extend_from_slice(&self.0); + } + + #[inline] + fn as_ssz_bytes(&self) -> Vec { + self.0.to_vec() } } @@ -459,11 +517,7 @@ impl Encode for U256 { } fn ssz_append(&self, buf: &mut Vec) { - let n = ::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) { - let n = ::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] diff --git a/ssz/tests/tests.rs b/ssz/tests/tests.rs index 95b1248..139ad39 100644 --- a/ssz/tests/tests.rs +++ b/ssz/tests/tests.rs @@ -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,17 +48,25 @@ mod round_trip { } #[test] - fn h160() { - let items: Vec = vec![H160::zero(), H160::from([1; 20]), H160::random()]; + fn address() { + let items: Vec
= vec![ + Address::repeat_byte(0), + Address::from([1; 20]), + Address::random(), + ]; round_trip(items); } #[test] - fn vec_of_h160() { - let items: Vec> = vec![ + fn vec_of_address() { + let items: Vec> = 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); @@ -66,27 +74,27 @@ mod round_trip { #[test] fn h256() { - let items: Vec = vec![H256::zero(), H256::from([1; 32]), H256::random()]; + let items: Vec = vec![B256::repeat_byte(0), B256::from([1; 32]), B256::random()]; round_trip(items); } #[test] - fn vec_of_h256() { - let items: Vec> = vec![ + fn vec_of_b256() { + let items: Vec> = 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>> = vec![ + fn option_vec_b256() { + let items: Vec>> = 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()); } } diff --git a/ssz_derive/Cargo.toml b/ssz_derive/Cargo.toml index e820718..962053f 100644 --- a/ssz_derive/Cargo.toml +++ b/ssz_derive/Cargo.toml @@ -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"