diff --git a/crates/cluster/src/definition.rs b/crates/cluster/src/definition.rs index 9f3c30ae..5976573f 100644 --- a/crates/cluster/src/definition.rs +++ b/crates/cluster/src/definition.rs @@ -19,7 +19,7 @@ use pluto_eth2util::enr::{Record, RecordError}; use pluto_p2p::peer::{Peer, PeerError}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_with::{ - DefaultOnNull, DisplayFromStr, PickFirst, + DefaultOnNull, DeserializeAs, DisplayFromStr, PickFirst, Same, SerializeAs, base64::{Base64, Standard}, serde_as, }; @@ -95,6 +95,31 @@ pub struct Definition { pub definition_hash: Vec, } +struct DepositAmountsSerde; + +impl SerializeAs> for DepositAmountsSerde { + fn serialize_as(source: &Vec, serializer: S) -> Result + where + S: Serializer, + { + if source.is_empty() { + serializer.serialize_none() + } else { + source.serialize(serializer) + } + } +} + +impl<'de> DeserializeAs<'de, Vec> for DepositAmountsSerde { + fn deserialize_as(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + type Inner = DefaultOnNull>>; + Inner::deserialize_as(deserializer) + } +} + impl Serialize for Definition { fn serialize(&self, serializer: S) -> Result where @@ -1292,7 +1317,7 @@ pub struct DefinitionV1x8 { pub fork_version: Vec, /// DepositAmounts specifies partial deposit amounts that sum up to at least /// 32ETH. - #[serde_as(as = "DefaultOnNull>>")] + #[serde_as(as = "DepositAmountsSerde")] pub deposit_amounts: Vec, /// ConfigHash uniquely identifies a cluster definition excluding operator /// ENRs and signatures. @@ -1396,7 +1421,7 @@ pub struct DefinitionV1x9 { pub fork_version: Vec, /// DepositAmounts specifies partial deposit amounts that sum up to at least /// 32ETH. - #[serde_as(as = "DefaultOnNull>>")] + #[serde_as(as = "DepositAmountsSerde")] pub deposit_amounts: Vec, /// ConsensusProtocol is the consensus protocol name preferred by the /// cluster, e.g. "abft". @@ -1505,7 +1530,7 @@ pub struct DefinitionV1x10 { pub fork_version: Vec, /// Partial deposit amounts that sum up to at least /// 32ETH. - #[serde_as(as = "DefaultOnNull>>")] + #[serde_as(as = "DepositAmountsSerde")] pub deposit_amounts: Vec, /// Consensus protocol name preferred by the /// cluster, e.g. "abft". @@ -1856,6 +1881,43 @@ mod tests { assert!(definition.verify_hashes().is_ok()); } + #[test] + fn definition_empty_deposit_amounts_serialize_as_null() { + let mut definition = Definition { + version: V1_10.to_string(), + ..Default::default() + }; + + let value = serde_json::to_value(&definition).unwrap(); + assert_eq!(value["deposit_amounts"], serde_json::Value::Null); + + definition.deposit_amounts = vec![16_000_000_000, 16_000_000_000]; + let value = serde_json::to_value(&definition).unwrap(); + assert_eq!( + value["deposit_amounts"], + serde_json::json!([16_000_000_000u64, 16_000_000_000u64]) + ); + + let mut value = serde_json::to_value(&definition).unwrap(); + value["deposit_amounts"] = serde_json::Value::Null; + let definition = serde_json::from_value::(value).unwrap(); + assert!(definition.deposit_amounts.is_empty()); + } + + #[test] + fn definition_deposit_amounts_deserialize_string_array() { + let mut value = serde_json::to_value(Definition { + version: V1_10.to_string(), + ..Default::default() + }) + .unwrap(); + value["deposit_amounts"] = serde_json::json!(["16000000000", "16000000000"]); + + let definition = serde_json::from_value::(value).unwrap(); + + assert_eq!(definition.deposit_amounts, vec![16_000_000_000; 2]); + } + #[test] fn cluster_definition_incorrect_version() { let json_str = include_str!("testdata/cluster_definition_incorrect_version.json"); diff --git a/crates/cluster/src/lock.rs b/crates/cluster/src/lock.rs index 4b918204..879d880b 100644 --- a/crates/cluster/src/lock.rs +++ b/crates/cluster/src/lock.rs @@ -927,6 +927,18 @@ mod tests { assert_eq!(lock, deserialized); } + #[test] + fn serialize_lock_empty_deposit_amounts_as_null() { + let (lock, ..) = crate::test_cluster::new_for_test(1, 2, 3, 1); + + assert!(lock.definition.deposit_amounts.is_empty()); + let value = serde_json::to_value(&lock).unwrap(); + assert_eq!( + value["cluster_definition"]["deposit_amounts"], + serde_json::Value::Null + ); + } + #[test] fn cluster_lock_v1_10_0() { let json_str = include_str!("testdata/cluster_lock_v1_10_0.json");