Remove context_deserialize and import from crates.io (#8172)

Use the recently published `context_deserialize` and remove it from Lighthouse


Co-Authored-By: Mac L <mjladson@pm.me>

Co-Authored-By: Michael Sproul <michael@sigmaprime.io>
This commit is contained in:
Mac L
2025-11-28 01:53:46 +04:00
committed by GitHub
parent e291955400
commit 847fa3f034
11 changed files with 32 additions and 498 deletions

46
Cargo.lock generated
View File

@@ -2076,22 +2076,21 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "context_deserialize"
version = "0.1.0"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c5f9ea0a0ae2de4943f5ca71590b6dbd0b952475f0a0cafb30a470cec78c8b9"
dependencies = [
"context_deserialize_derive",
"milhouse",
"serde",
"ssz_types",
]
[[package]]
name = "context_deserialize_derive"
version = "0.1.0"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c57b2db1e4e3ed804dcc49894a144b68fe6c754b8f545eb1dda7ad3c7dbe7e6"
dependencies = [
"context_deserialize",
"quote",
"serde",
"serde_json",
"syn 1.0.109",
]
@@ -3258,9 +3257,9 @@ dependencies = [
[[package]]
name = "ethereum_hashing"
version = "0.7.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c853bd72c9e5787f8aafc3df2907c2ed03cff3150c3acd94e2e53a98ab70a8ab"
checksum = "5aa93f58bb1eb3d1e556e4f408ef1dac130bad01ac37db4e7ade45de40d1c86a"
dependencies = [
"cpufeatures",
"ring",
@@ -3282,12 +3281,13 @@ dependencies = [
[[package]]
name = "ethereum_ssz"
version = "0.9.1"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dcddb2554d19cde19b099fadddde576929d7a4d0c1cd3512d1fd95cf174375c"
checksum = "7e8cd8c4f47dfb947dbfe3cdf2945ae1da808dbedc592668658e827a12659ba1"
dependencies = [
"alloy-primitives",
"arbitrary",
"context_deserialize",
"ethereum_serde_utils",
"itertools 0.13.0",
"serde",
@@ -3298,9 +3298,9 @@ dependencies = [
[[package]]
name = "ethereum_ssz_derive"
version = "0.9.1"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a657b6b3b7e153637dc6bdc6566ad9279d9ee11a15b12cfb24a2e04360637e9f"
checksum = "78d247bc40823c365a62e572441a8f8b12df03f171713f06bc76180fcd56ab71"
dependencies = [
"darling 0.20.11",
"proc-macro2",
@@ -5858,12 +5858,13 @@ dependencies = [
[[package]]
name = "milhouse"
version = "0.7.0"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bdb104e38d3a8c5ffb7e9d2c43c522e6bcc34070edbadba565e722f0dee56c7"
checksum = "259dd9da2ae5e0278b95da0b7ecef9c18c309d0a2d9e6db57ed33b9e8910c5e7"
dependencies = [
"alloy-primitives",
"arbitrary",
"context_deserialize",
"educe",
"ethereum_hashing",
"ethereum_ssz",
@@ -8539,11 +8540,12 @@ dependencies = [
[[package]]
name = "ssz_types"
version = "0.12.2"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "704671195db617afa3d919da8f220f2535f20d0fa8dad96a1c27a38a5f8f6e9c"
checksum = "1fc20a89bab2dabeee65e9c9eb96892dc222c23254b401e1319b85efd852fa31"
dependencies = [
"arbitrary",
"context_deserialize",
"ethereum_serde_utils",
"ethereum_ssz",
"itertools 0.14.0",
@@ -9441,9 +9443,9 @@ dependencies = [
[[package]]
name = "tree_hash"
version = "0.10.0"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee44f4cef85f88b4dea21c0b1f58320bdf35715cf56d840969487cff00613321"
checksum = "2db21caa355767db4fd6129876e5ae278a8699f4a6959b1e3e7aff610b532d52"
dependencies = [
"alloy-primitives",
"ethereum_hashing",
@@ -9454,11 +9456,11 @@ dependencies = [
[[package]]
name = "tree_hash_derive"
version = "0.10.0"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bee2ea1551f90040ab0e34b6fb7f2fa3bad8acc925837ac654f2c78a13e3089"
checksum = "711cc655fcbb48384a87dc2bf641b991a15c5ad9afc3caa0b1ab1df3b436f70f"
dependencies = [
"darling 0.20.11",
"darling 0.21.3",
"proc-macro2",
"quote",
"syn 2.0.110",

View File

@@ -47,8 +47,6 @@ members = [
"common/validator_dir",
"common/warp_utils",
"common/workspace_members",
"consensus/context_deserialize/context_deserialize",
"consensus/context_deserialize/context_deserialize_derive",
"consensus/fixed_bytes",
"consensus/fork_choice",
"consensus/int_to_bytes",
@@ -122,10 +120,7 @@ clap = { version = "4.5.4", features = ["derive", "cargo", "wrap_help"] }
clap_utils = { path = "common/clap_utils" }
compare_fields = "0.1"
console-subscriber = "0.4"
context_deserialize = { path = "consensus/context_deserialize/context_deserialize", features = [
"all",
] }
context_deserialize_derive = { path = "consensus/context_deserialize/context_deserialize_derive" }
context_deserialize = "0.2"
criterion = "0.5"
delay_map = "0.4"
deposit_contract = { path = "common/deposit_contract" }
@@ -143,10 +138,10 @@ eth2_key_derivation = { path = "crypto/eth2_key_derivation" }
eth2_keystore = { path = "crypto/eth2_keystore" }
eth2_network_config = { path = "common/eth2_network_config" }
eth2_wallet = { path = "crypto/eth2_wallet" }
ethereum_hashing = "0.7.0"
ethereum_hashing = "0.8.0"
ethereum_serde_utils = "0.8.0"
ethereum_ssz = "0.9.0"
ethereum_ssz_derive = "0.9.0"
ethereum_ssz = { version = "0.10.0", features = ["context_deserialize"] }
ethereum_ssz_derive = "0.10.0"
execution_layer = { path = "beacon_node/execution_layer" }
exit-future = "0.2"
filesystem = { path = "common/filesystem" }
@@ -183,7 +178,7 @@ malloc_utils = { path = "common/malloc_utils" }
maplit = "1"
merkle_proof = { path = "consensus/merkle_proof" }
metrics = { path = "common/metrics" }
milhouse = { version = "0.7", default-features = false }
milhouse = { version = "0.9", default-features = false, features = ["context_deserialize"] }
mockall = "0.13"
mockall_double = "0.3"
mockito = "1.5.0"
@@ -232,7 +227,7 @@ slashing_protection = { path = "validator_client/slashing_protection" }
slot_clock = { path = "common/slot_clock" }
smallvec = { version = "1.11.2", features = ["arbitrary"] }
snap = "1"
ssz_types = "0.12.2"
ssz_types = { version = "0.14.0", features = ["context_deserialize"] }
state_processing = { path = "consensus/state_processing" }
store = { path = "beacon_node/store" }
strum = { version = "0.24", features = ["derive"] }
@@ -257,8 +252,8 @@ tracing-core = "0.1"
tracing-log = "0.2"
tracing-opentelemetry = "0.31.0"
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
tree_hash = "0.10.0"
tree_hash_derive = "0.10.0"
tree_hash = "0.12.0"
tree_hash_derive = "0.12.0"
types = { path = "consensus/types" }
url = "2"
uuid = { version = "0.8", features = ["serde", "v4"] }

View File

@@ -1,17 +0,0 @@
[package]
name = "context_deserialize"
version = "0.1.0"
edition = "2021"
[features]
default = ["derive"]
derive = ["dep:context_deserialize_derive"]
milhouse = ["dep:milhouse"]
ssz = ["dep:ssz_types"]
all = ["derive", "milhouse", "ssz"]
[dependencies]
context_deserialize_derive = { version = "0.1.0", path = "../context_deserialize_derive", optional = true }
milhouse = { workspace = true, optional = true }
serde = { workspace = true }
ssz_types = { workspace = true, optional = true }

View File

@@ -1,103 +0,0 @@
use crate::ContextDeserialize;
use serde::de::{Deserialize, DeserializeSeed, Deserializer, SeqAccess, Visitor};
use std::marker::PhantomData;
use std::sync::Arc;
impl<'de, C, T> ContextDeserialize<'de, T> for Arc<C>
where
C: ContextDeserialize<'de, T>,
{
fn context_deserialize<D>(deserializer: D, context: T) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Arc::new(C::context_deserialize(deserializer, context)?))
}
}
impl<'de, T, C> ContextDeserialize<'de, C> for Vec<T>
where
T: ContextDeserialize<'de, C>,
C: Clone,
{
fn context_deserialize<D>(deserializer: D, context: C) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
// Our Visitor, which owns one copy of the context T
struct ContextVisitor<C, T> {
context: T,
_marker: PhantomData<C>,
}
impl<'de, C, T> Visitor<'de> for ContextVisitor<C, T>
where
C: ContextDeserialize<'de, T>,
T: Clone,
{
type Value = Vec<C>;
fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
fmt.write_str("a sequence of contextdeserialized elements")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Vec<C>, A::Error>
where
A: SeqAccess<'de>,
{
let mut out = Vec::with_capacity(seq.size_hint().unwrap_or(0));
// for each element, we clone the context and hand it to the seed
while let Some(elem) = seq.next_element_seed(ContextSeed {
context: self.context.clone(),
_marker: PhantomData,
})? {
out.push(elem);
}
Ok(out)
}
}
// A little seed that hands the deserializer + context into C::context_deserialize
struct ContextSeed<T, C> {
context: C,
_marker: PhantomData<T>,
}
impl<'de, T, C> DeserializeSeed<'de> for ContextSeed<T, C>
where
T: ContextDeserialize<'de, C>,
C: Clone,
{
type Value = T;
fn deserialize<D>(self, deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
{
T::context_deserialize(deserializer, self.context)
}
}
deserializer.deserialize_seq(ContextVisitor {
context,
_marker: PhantomData,
})
}
}
macro_rules! trivial_deserialize {
($($t:ty),* $(,)?) => {
$(
impl<'de, T> ContextDeserialize<'de, T> for $t {
fn context_deserialize<D>(deserializer: D, _context: T) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
<$t>::deserialize(deserializer)
}
}
)*
};
}
trivial_deserialize!(bool, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, f32, f64);

View File

@@ -1,45 +0,0 @@
use crate::ContextDeserialize;
use milhouse::{List, Value, Vector};
use serde::de::Deserializer;
use ssz_types::typenum::Unsigned;
impl<'de, C, T, N> ContextDeserialize<'de, C> for List<T, N>
where
T: ContextDeserialize<'de, C> + Value,
N: Unsigned,
C: Clone,
{
fn context_deserialize<D>(deserializer: D, context: C) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
// First deserialize as a Vec.
// This is not the most efficient implementation as it allocates a temporary Vec. In future
// we could write a more performant implementation using `List::builder()`.
let vec = Vec::<T>::context_deserialize(deserializer, context)?;
// Then convert to List, which will check the length.
List::new(vec)
.map_err(|e| serde::de::Error::custom(format!("Failed to create List: {:?}", e)))
}
}
impl<'de, C, T, N> ContextDeserialize<'de, C> for Vector<T, N>
where
T: ContextDeserialize<'de, C> + Value,
N: Unsigned,
C: Clone,
{
fn context_deserialize<D>(deserializer: D, context: C) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
// First deserialize as a List
let list = List::<T, N>::context_deserialize(deserializer, context)?;
// Then convert to Vector, which will check the length
Vector::try_from(list).map_err(|e| {
serde::de::Error::custom(format!("Failed to convert List to Vector: {:?}", e))
})
}
}

View File

@@ -1,7 +0,0 @@
mod core;
#[cfg(feature = "milhouse")]
mod milhouse;
#[cfg(feature = "ssz")]
mod ssz;

View File

@@ -1,51 +0,0 @@
use crate::ContextDeserialize;
use serde::{
de::{Deserializer, Error},
Deserialize,
};
use ssz_types::{
length::{Fixed, Variable},
typenum::Unsigned,
Bitfield, FixedVector,
};
impl<'de, C, T, N> ContextDeserialize<'de, C> for FixedVector<T, N>
where
T: ContextDeserialize<'de, C>,
N: Unsigned,
C: Clone,
{
fn context_deserialize<D>(deserializer: D, context: C) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let vec = Vec::<T>::context_deserialize(deserializer, context)?;
FixedVector::new(vec).map_err(|e| D::Error::custom(format!("{:?}", e)))
}
}
impl<'de, C, N> ContextDeserialize<'de, C> for Bitfield<Variable<N>>
where
N: Unsigned + Clone,
{
fn context_deserialize<D>(deserializer: D, _context: C) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Bitfield::<Variable<N>>::deserialize(deserializer)
.map_err(|e| D::Error::custom(format!("{:?}", e)))
}
}
impl<'de, C, N> ContextDeserialize<'de, C> for Bitfield<Fixed<N>>
where
N: Unsigned + Clone,
{
fn context_deserialize<D>(deserializer: D, _context: C) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Bitfield::<Fixed<N>>::deserialize(deserializer)
.map_err(|e| D::Error::custom(format!("{:?}", e)))
}
}

View File

@@ -1,13 +0,0 @@
mod impls;
#[cfg(feature = "derive")]
pub use context_deserialize_derive::context_deserialize;
use serde::de::Deserializer;
/// General-purpose deserialization trait that accepts extra context `C`.
pub trait ContextDeserialize<'de, C>: Sized {
fn context_deserialize<D>(deserializer: D, context: C) -> Result<Self, D::Error>
where
D: Deserializer<'de>;
}

View File

@@ -1,16 +0,0 @@
[package]
name = "context_deserialize_derive"
version = "0.1.0"
edition = "2021"
[lib]
proc-macro = true
[dependencies]
quote = { workspace = true }
syn = { workspace = true }
[dev-dependencies]
context_deserialize = { path = "../context_deserialize" }
serde = { workspace = true }
serde_json = "1.0"

View File

@@ -1,118 +0,0 @@
extern crate proc_macro;
extern crate quote;
extern crate syn;
use proc_macro::TokenStream;
use quote::quote;
use syn::{
parse_macro_input, AttributeArgs, DeriveInput, GenericParam, LifetimeDef, Meta, NestedMeta,
WhereClause,
};
#[proc_macro_attribute]
pub fn context_deserialize(attr: TokenStream, item: TokenStream) -> TokenStream {
let args = parse_macro_input!(attr as AttributeArgs);
let input = parse_macro_input!(item as DeriveInput);
let ident = &input.ident;
let mut ctx_types = Vec::new();
let mut explicit_where: Option<WhereClause> = None;
for meta in args {
match meta {
NestedMeta::Meta(Meta::Path(p)) => {
ctx_types.push(p);
}
NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("bound") => {
if let syn::Lit::Str(lit_str) = &nv.lit {
let where_string = format!("where {}", lit_str.value());
match syn::parse_str::<WhereClause>(&where_string) {
Ok(where_clause) => {
explicit_where = Some(where_clause);
}
Err(err) => {
return syn::Error::new_spanned(
lit_str,
format!("Invalid where clause '{}': {}", lit_str.value(), err),
)
.to_compile_error()
.into();
}
}
} else {
return syn::Error::new_spanned(
&nv,
"Expected a string literal for `bound` value",
)
.to_compile_error()
.into();
}
}
_ => {
return syn::Error::new_spanned(
&meta,
"Expected paths or `bound = \"...\"` in #[context_deserialize(...)]",
)
.to_compile_error()
.into();
}
}
}
if ctx_types.is_empty() {
return quote! {
compile_error!("Usage: #[context_deserialize(Type1, Type2, ..., bound = \"...\")]");
}
.into();
}
let original_generics = input.generics.clone();
// Clone and clean generics for impl use (remove default params)
let mut impl_generics = input.generics.clone();
for param in impl_generics.params.iter_mut() {
if let GenericParam::Type(ty) = param {
ty.eq_token = None;
ty.default = None;
}
}
// Ensure 'de lifetime exists in impl generics
let has_de = impl_generics
.lifetimes()
.any(|LifetimeDef { lifetime, .. }| lifetime.ident == "de");
if !has_de {
impl_generics.params.insert(0, syn::parse_quote! { 'de });
}
let (_, ty_generics, _) = original_generics.split_for_impl();
let (impl_gens, _, _) = impl_generics.split_for_impl();
// Generate: no `'de` applied to the type name
let mut impls = quote! {};
for ctx in ctx_types {
impls.extend(quote! {
impl #impl_gens context_deserialize::ContextDeserialize<'de, #ctx>
for #ident #ty_generics
#explicit_where
{
fn context_deserialize<D>(
deserializer: D,
_context: #ctx,
) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
<Self as serde::Deserialize>::deserialize(deserializer)
}
}
});
}
quote! {
#input
#impls
}
.into()
}

View File

@@ -1,93 +0,0 @@
use context_deserialize::{context_deserialize, ContextDeserialize};
use serde::{Deserialize, Serialize};
#[test]
fn test_context_deserialize_derive() {
type TestContext = ();
#[context_deserialize(TestContext)]
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Test {
field: String,
}
let test = Test {
field: "test".to_string(),
};
let serialized = serde_json::to_string(&test).unwrap();
let deserialized =
Test::context_deserialize(&mut serde_json::Deserializer::from_str(&serialized), ())
.unwrap();
assert_eq!(test, deserialized);
}
#[test]
fn test_context_deserialize_derive_multiple_types() {
#[allow(dead_code)]
struct TestContext1(u64);
#[allow(dead_code)]
struct TestContext2(String);
// This will derive:
// - ContextDeserialize<TestContext1> for Test
// - ContextDeserialize<TestContext2> for Test
// by just leveraging the Deserialize impl
#[context_deserialize(TestContext1, TestContext2)]
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Test {
field: String,
}
let test = Test {
field: "test".to_string(),
};
let serialized = serde_json::to_string(&test).unwrap();
let deserialized = Test::context_deserialize(
&mut serde_json::Deserializer::from_str(&serialized),
TestContext1(1),
)
.unwrap();
assert_eq!(test, deserialized);
let deserialized = Test::context_deserialize(
&mut serde_json::Deserializer::from_str(&serialized),
TestContext2("2".to_string()),
)
.unwrap();
assert_eq!(test, deserialized);
}
#[test]
fn test_context_deserialize_derive_bound() {
use std::fmt::Debug;
struct TestContext;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Inner {
value: u64,
}
#[context_deserialize(
TestContext,
bound = "T: Serialize + for<'a> Deserialize<'a> + Debug + PartialEq"
)]
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Wrapper<T> {
inner: T,
}
let val = Wrapper {
inner: Inner { value: 42 },
};
let serialized = serde_json::to_string(&val).unwrap();
let deserialized = Wrapper::<Inner>::context_deserialize(
&mut serde_json::Deserializer::from_str(&serialized),
TestContext,
)
.unwrap();
assert_eq!(val, deserialized);
}