diff --git a/Cargo.toml b/Cargo.toml index 4c9ded03f..eb15e4969 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ serde_yaml = "0.8" itertools = "0.10.3" thiserror = "1.0.30" hex = "0.4.3" -ssz_rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "13d2c7d1a0dc35a6dfbc2d657d5db95080cb44c0" } +ssz_rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "9a238ce6385b6fe7745a7c7ff0c42a9315628da1" } blst = "0.3.11" rand = "0.8.4" sha2 = "0.10.8" diff --git a/beacon-api-client/examples/get_block.rs b/beacon-api-client/examples/get_block.rs index 67f39f870..b6b227e1a 100644 --- a/beacon-api-client/examples/get_block.rs +++ b/beacon-api-client/examples/get_block.rs @@ -1,5 +1,6 @@ use beacon_api_client::{mainnet::Client, BlockId}; use ethereum_consensus::primitives::Root; +use hex::FromHex; use url::Url; #[tokio::main] @@ -7,9 +8,8 @@ async fn main() { let url = Url::parse("http://localhost:5052").unwrap(); let client = Client::new(url); - let root_hex = - hex::decode("421c16805c3416150aa04533fdfe7fc3f0880d0ed86cee33fa58011f10dd95c8").unwrap(); - let root = Root::try_from(root_hex.as_ref()).unwrap(); + let root_hex = "421c16805c3416150aa04533fdfe7fc3f0880d0ed86cee33fa58011f10dd95c8"; + let root = Root::from_hex(root_hex).unwrap(); let id = BlockId::Root(root); let block = client.get_beacon_block(id).await.unwrap(); diff --git a/beacon-api-client/src/types.rs b/beacon-api-client/src/types.rs index 17757754a..4eb7b09da 100644 --- a/beacon-api-client/src/types.rs +++ b/beacon-api-client/src/types.rs @@ -91,7 +91,7 @@ impl FromStr for StateId { Ok(slot) => Ok(Self::Slot(slot)), Err(_) => match try_bytes_from_hex_str(s) { Ok(root_data) => { - let root = Root::try_from(root_data.as_ref()).map_err(|err| format!("could not parse state identifier by root from the provided argument {s}: {err}"))?; + let root = Root::try_from(root_data.as_slice()).map_err(|err| format!("could not parse state identifier by root from the provided argument {s}: {err}"))?; Ok(Self::Root(root)) } Err(err) => { diff --git a/ethereum-consensus/src/altair/block_processing.rs b/ethereum-consensus/src/altair/block_processing.rs index e5b827d5d..aa928cbaa 100644 --- a/ethereum-consensus/src/altair/block_processing.rs +++ b/ethereum-consensus/src/altair/block_processing.rs @@ -189,16 +189,7 @@ pub fn process_deposit< let root = state.eth1_data.deposit_root; if is_valid_merkle_branch(leaf, branch, depth, index, root).is_err() { return Err(invalid_operation_error(InvalidOperation::Deposit( - InvalidDeposit::InvalidProof { - leaf, - branch: branch - .iter() - .map(|node| Node::try_from(node.as_ref()).expect("correct size")) - .collect(), - depth, - index, - root, - }, + InvalidDeposit::InvalidProof { leaf, branch: branch.to_vec(), depth, index, root }, ))) } diff --git a/ethereum-consensus/src/bellatrix/spec/mod.rs b/ethereum-consensus/src/bellatrix/spec/mod.rs index ef42ae3f8..bfb8ea0a2 100644 --- a/ethereum-consensus/src/bellatrix/spec/mod.rs +++ b/ethereum-consensus/src/bellatrix/spec/mod.rs @@ -216,16 +216,7 @@ pub fn process_deposit< let root = state.eth1_data.deposit_root; if is_valid_merkle_branch(leaf, branch, depth, index, root).is_err() { return Err(invalid_operation_error(InvalidOperation::Deposit( - InvalidDeposit::InvalidProof { - leaf, - branch: branch - .iter() - .map(|node| Node::try_from(node.as_ref()).expect("correct size")) - .collect(), - depth, - index, - root, - }, + InvalidDeposit::InvalidProof { leaf, branch: branch.to_vec(), depth, index, root }, ))); } state.eth1_deposit_index += 1; diff --git a/ethereum-consensus/src/capella/spec/mod.rs b/ethereum-consensus/src/capella/spec/mod.rs index 21e8c9735..94340bc32 100644 --- a/ethereum-consensus/src/capella/spec/mod.rs +++ b/ethereum-consensus/src/capella/spec/mod.rs @@ -223,16 +223,7 @@ pub fn process_deposit< let root = state.eth1_data.deposit_root; if is_valid_merkle_branch(leaf, branch, depth, index, root).is_err() { return Err(invalid_operation_error(InvalidOperation::Deposit( - InvalidDeposit::InvalidProof { - leaf, - branch: branch - .iter() - .map(|node| Node::try_from(node.as_ref()).expect("correct size")) - .collect(), - depth, - index, - root, - }, + InvalidDeposit::InvalidProof { leaf, branch: branch.to_vec(), depth, index, root }, ))); } state.eth1_deposit_index += 1; diff --git a/ethereum-consensus/src/deneb/blob_sidecar.rs b/ethereum-consensus/src/deneb/blob_sidecar.rs index 8274c6cd3..a7942600e 100644 --- a/ethereum-consensus/src/deneb/blob_sidecar.rs +++ b/ethereum-consensus/src/deneb/blob_sidecar.rs @@ -4,7 +4,7 @@ use crate::{ polynomial_commitments::{KzgCommitment, KzgProof}, SignedBeaconBlockHeader, }, - primitives::{BlobIndex, Bytes32, Root}, + primitives::{BlobIndex, Root}, ssz::prelude::*, Error, }; @@ -27,7 +27,7 @@ pub struct BlobSidecar< pub kzg_commitment: KzgCommitment, pub kzg_proof: KzgProof, pub signed_block_header: SignedBeaconBlockHeader, - pub kzg_commitment_inclusion_proof: Vector, + pub kzg_commitment_inclusion_proof: Vector, } pub fn verify_blob_sidecar_inclusion_proof< diff --git a/ethereum-consensus/src/deneb/spec/mod.rs b/ethereum-consensus/src/deneb/spec/mod.rs index 3707ad6c1..a5471bfb5 100644 --- a/ethereum-consensus/src/deneb/spec/mod.rs +++ b/ethereum-consensus/src/deneb/spec/mod.rs @@ -381,16 +381,7 @@ pub fn process_deposit< let root = state.eth1_data.deposit_root; if is_valid_merkle_branch(leaf, branch, depth, index, root).is_err() { return Err(invalid_operation_error(InvalidOperation::Deposit( - InvalidDeposit::InvalidProof { - leaf, - branch: branch - .iter() - .map(|node| Node::try_from(node.as_ref()).expect("correct size")) - .collect(), - depth, - index, - root, - }, + InvalidDeposit::InvalidProof { leaf, branch: branch.to_vec(), depth, index, root }, ))); } state.eth1_deposit_index += 1; diff --git a/ethereum-consensus/src/phase0/block_processing.rs b/ethereum-consensus/src/phase0/block_processing.rs index 43b5b47cb..9763867be 100644 --- a/ethereum-consensus/src/phase0/block_processing.rs +++ b/ethereum-consensus/src/phase0/block_processing.rs @@ -342,10 +342,7 @@ pub fn process_deposit< return Err(invalid_operation_error(InvalidOperation::Deposit( InvalidDeposit::InvalidProof { leaf, - branch: branch - .iter() - .map(|node| Node::try_from(node.as_ref()).expect("correct size")) - .collect(), + branch: branch.to_vec(), depth: DEPOSIT_MERKLE_DEPTH, index, root, diff --git a/ethereum-consensus/src/phase0/operations.rs b/ethereum-consensus/src/phase0/operations.rs index 8f5d89faa..6b33d5f9f 100644 --- a/ethereum-consensus/src/phase0/operations.rs +++ b/ethereum-consensus/src/phase0/operations.rs @@ -117,7 +117,7 @@ const DEPOSIT_PROOF_LENGTH: usize = get_deposit_proof_length(); Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, )] pub struct Deposit { - pub proof: Vector, + pub proof: Vector, pub data: DepositData, } diff --git a/ethereum-consensus/src/serde.rs b/ethereum-consensus/src/serde.rs index 4c8f20207..26f5e46bf 100644 --- a/ethereum-consensus/src/serde.rs +++ b/ethereum-consensus/src/serde.rs @@ -1,4 +1,95 @@ -pub use ssz_rs::serde::{as_hex, as_str, try_bytes_from_hex_str}; +use hex::FromHexError; +use std::fmt; + +const HEX_ENCODING_PREFIX: &str = "0x"; + +#[inline] +fn write_hex_from_bytes>(f: &mut fmt::Formatter<'_>, data: D) -> fmt::Result { + for i in data.as_ref() { + write!(f, "{i:02x}")?; + } + Ok(()) +} + +pub fn write_bytes_to_lower_hex>( + f: &mut fmt::Formatter<'_>, + data: T, +) -> fmt::Result { + write!(f, "0x")?; + write_hex_from_bytes(f, data) +} + +pub fn write_bytes_to_lower_hex_display + ExactSizeIterator>( + f: &mut fmt::Formatter<'_>, + data: T, +) -> fmt::Result { + let len = data.len(); + let (first, last) = if len >= 4 { ((0..2), Some(len - 2..len)) } else { ((0..len), None) }; + let data = data.as_ref(); + write!(f, "0x")?; + write_hex_from_bytes(f, &data[first])?; + if let Some(last) = last { + write!(f, "…")?; + write_hex_from_bytes(f, &data[last])?; + } + Ok(()) +} + +pub fn try_bytes_from_hex_str(s: &str) -> Result, FromHexError> { + let target = s.strip_prefix(HEX_ENCODING_PREFIX).unwrap_or(s); + let data = hex::decode(target)?; + Ok(data) +} + +pub mod as_hex { + use super::*; + use serde::Deserialize; + use std::fmt::Display; + + pub fn serialize>(data: T, serializer: S) -> Result + where + S: serde::Serializer, + { + let encoding = hex::encode(data.as_ref()); + let output = format!("{HEX_ENCODING_PREFIX}{encoding}"); + serializer.collect_str(&output) + } + + pub fn deserialize<'de, D, T, E>(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + T: for<'a> TryFrom<&'a [u8], Error = E> + fmt::Debug, + E: Display, + { + let str = String::deserialize(deserializer)?; + + let data = try_bytes_from_hex_str(&str).map_err(serde::de::Error::custom)?; + + T::try_from(&data).map_err(serde::de::Error::custom) + } +} + +pub mod as_str { + use serde::Deserialize; + use std::{fmt::Display, str::FromStr}; + + pub fn serialize(data: T, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.collect_str(&data.to_string()) + } + + pub fn deserialize<'de, D, T, E>(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + T: FromStr, + E: Display, + { + let s = String::deserialize(deserializer)?; + T::from_str(&s).map_err(serde::de::Error::custom) + } +} pub mod seq_of_str { use serde::{ @@ -59,6 +150,7 @@ pub mod seq_of_str { #[cfg(test)] mod tests { + use super::*; use crate::types::mainnet::SignedBeaconBlock; const EXPECTED_SIGNED_BLOCK_STR: &str = r#" @@ -120,4 +212,27 @@ mod tests { let recovered_signed_block: SignedBeaconBlock = serde_json::from_str(&str).unwrap(); assert_eq!(signed_block, recovered_signed_block); } + + struct Fmt(Vec); + + impl fmt::Debug for Fmt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write_bytes_to_lower_hex(f, self.0.iter()) + } + } + + impl fmt::Display for Fmt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } + } + + #[test] + fn test_fmt() { + let data = Fmt((0u8..3).collect::>()); + let s = format!("{data:?}"); + assert_eq!(s, "0x000102"); + let s = format!("{data}"); + assert_eq!(s, "0x000102"); + } } diff --git a/ethereum-consensus/src/ssz/byte_list.rs b/ethereum-consensus/src/ssz/byte_list.rs index c6cd98ac9..e08a81774 100644 --- a/ethereum-consensus/src/ssz/byte_list.rs +++ b/ethereum-consensus/src/ssz/byte_list.rs @@ -1,5 +1,7 @@ -use crate::ssz::prelude::*; -use ssz_rs::utils::{write_bytes_to_lower_hex, write_bytes_to_lower_hex_display}; +use crate::{ + serde::{write_bytes_to_lower_hex, write_bytes_to_lower_hex_display}, + ssz::prelude::*, +}; use std::{ fmt, hash::{Hash, Hasher}, diff --git a/ethereum-consensus/src/ssz/byte_vector.rs b/ethereum-consensus/src/ssz/byte_vector.rs index 95cfb2c4b..bf5f7329c 100644 --- a/ethereum-consensus/src/ssz/byte_vector.rs +++ b/ethereum-consensus/src/ssz/byte_vector.rs @@ -1,5 +1,7 @@ -use crate::ssz::prelude::*; -use ssz_rs::utils::{write_bytes_to_lower_hex, write_bytes_to_lower_hex_display}; +use crate::{ + serde::{write_bytes_to_lower_hex, write_bytes_to_lower_hex_display}, + ssz::prelude::*, +}; use std::{ fmt, hash::{Hash, Hasher}, diff --git a/justfile b/justfile index f9708afc1..0728b94e9 100644 --- a/justfile +++ b/justfile @@ -8,8 +8,8 @@ gen-types: test: cargo test --all-features --all-targets --workspace --exclude spec-tests -run-spec-tests: - cargo test -p spec-tests +run-spec-tests filter="": + cargo test -p spec-tests {{filter}} fmt: cargo +nightly fmt --all lint: fmt diff --git a/spec-tests/runners/light_client.rs b/spec-tests/runners/light_client.rs index 7b5af83f3..d07e641d9 100644 --- a/spec-tests/runners/light_client.rs +++ b/spec-tests/runners/light_client.rs @@ -9,7 +9,7 @@ use ethereum_consensus::Error as SpecError; use serde::Deserialize; use ssz_rs::{ prelude::*, - proofs::{get_subtree_index, is_valid_merkle_branch_for_generalized_index, log_2, prove}, + proofs::{get_subtree_index, is_valid_merkle_branch_for_generalized_index, log_2}, }; #[derive(Debug, Deserialize)] @@ -42,7 +42,7 @@ fn path_from(meta: &TestMeta) -> Vec { pub fn run_test(mut object: O, path: Path, proof: &Proof) -> Result<(), Error> { let root = object.hash_tree_root().unwrap(); // test proof matches - let (computed_proof, witness) = prove(&mut object, path).expect("can prove"); + let (computed_proof, witness) = object.prove(path).expect("can prove"); assert_eq!(root, witness); assert_eq!(proof.leaf, computed_proof.leaf); assert_eq!(proof.leaf_index, computed_proof.index); diff --git a/spec-tests/runners/ssz_static.rs b/spec-tests/runners/ssz_static.rs index 3d9f73004..407331b56 100644 --- a/spec-tests/runners/ssz_static.rs +++ b/spec-tests/runners/ssz_static.rs @@ -3,14 +3,14 @@ use crate::{ test_case::TestCase, test_utils::{load_snappy_ssz_bytes, load_yaml, Error}, }; -use ethereum_consensus::{primitives::Bytes32, state_transition::Context}; +use ethereum_consensus::{primitives::Root, state_transition::Context}; use serde::Deserialize; use ssz_rs::prelude::*; use std::path::Path; #[derive(Deserialize)] struct RootData { - root: Bytes32, + root: Root, } fn load_test(test_case_path: &str) -> (RootData, Vec) { @@ -31,7 +31,7 @@ fn run_test( let serialized = serialize(&decoded_data).unwrap(); let root = decoded_data.hash_tree_root().unwrap(); assert_eq!(serialized, encoding); - assert_eq!(root.as_ref(), data.root.as_ref()); + assert_eq!(root, data.root); Ok(()) }