From c60ab3f60cf6f2522dcce221b7ffebdf65f4d0ab Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Tue, 27 May 2025 22:37:32 +0800 Subject: [PATCH 01/42] append string --- validator_client/src/cli.rs | 10 ++++++++++ validator_client/src/config.rs | 22 ++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/validator_client/src/cli.rs b/validator_client/src/cli.rs index cdbf9f8472b..f05fbb8a2a8 100644 --- a/validator_client/src/cli.rs +++ b/validator_client/src/cli.rs @@ -145,11 +145,21 @@ pub struct ValidatorClient { #[clap( long, value_name = "GRAFFITI", + conflicts_with = "graffiti_client", help = "Specify your custom graffiti to be included in blocks.", display_order = 0 )] pub graffiti: Option, + #[clap( + long, + value_name = "GRAFFITI", + conflicts_with = "graffiti", + help = "When used, client version info will be automatically appended to user custom graffiti.", + display_order = 0 + )] + pub graffiti_client: Option, + #[clap( long, value_name = "GRAFFITI-FILE", diff --git a/validator_client/src/config.rs b/validator_client/src/config.rs index 726aa96cf9d..c238de3be29 100644 --- a/validator_client/src/config.rs +++ b/validator_client/src/config.rs @@ -231,6 +231,28 @@ impl Config { } } + if let Some(input_graffiti) = validator_client_config.graffiti_client.as_ref() { + let client_version = get_engine_version(1); + + let string = "testing"; + + let combined = format!("{} {}", string, input_graffiti); + + let combined_bytes = combined.as_bytes(); + + if combined.len() > GRAFFITI_BYTES_LEN { + return Err(format!( + "Your graffiti is too long! Some bytes are reserved for client version. {} bytes maximum!", + GRAFFITI_BYTES_LEN - string.len() + )); + } else { + let mut graffiti = [0; 32]; + graffiti[..combined.len()].copy_from_slice(combined_bytes); + + config.graffiti = Some(graffiti.into()); + } + } + if let Some(input_fee_recipient) = validator_client_config.suggested_fee_recipient { config.validator_store.fee_recipient = Some(input_fee_recipient); } From 067b68d337d3ba37a2566a83136293115bd8e1c9 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Tue, 3 Jun 2025 22:17:55 +0800 Subject: [PATCH 02/42] Add GraffitiPolicy --- common/eth2/src/lib.rs | 21 +++++++++++++++++++ common/eth2/src/types.rs | 9 ++++++++ validator_client/src/config.rs | 2 -- .../validator_services/src/block_service.rs | 13 ++++++++++++ 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index fa3fa047831..a708cc9f117 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -2177,6 +2177,7 @@ impl BeaconNodeHttpClient { graffiti: Option<&Graffiti>, skip_randao_verification: SkipRandaoVerification, builder_booster_factor: Option, + graffiti_policy: GraffitiPolicy, // or ValidatorBlocksQuery ) -> Result { let mut path = self.eth_path(V3)?; @@ -2204,6 +2205,18 @@ impl BeaconNodeHttpClient { .append_pair("builder_boost_factor", &builder_booster_factor.to_string()); } + // Add graffiti policy + match graffiti_policy { + GraffitiPolicy::AppendClientVersions => { + path.query_pairs_mut() + .append_pair("graffity_policy", "append_client_versions"); + } + GraffitiPolicy::PreserveUserGraffiti => { + path.query_pairs_mut() + .append_pair("graffiti_policy", "preserve_user_graffiti"); + } + } + Ok(path) } @@ -2214,6 +2227,7 @@ impl BeaconNodeHttpClient { randao_reveal: &SignatureBytes, graffiti: Option<&Graffiti>, builder_booster_factor: Option, + graffiti_policy: GraffitiPolicy, // add graffitipolicy validator ) -> Result<(JsonProduceBlockV3Response, ProduceBlockV3Metadata), Error> { self.get_validator_blocks_v3_modular( slot, @@ -2221,6 +2235,7 @@ impl BeaconNodeHttpClient { graffiti, SkipRandaoVerification::No, builder_booster_factor, + graffiti_policy, ) .await } @@ -2233,6 +2248,7 @@ impl BeaconNodeHttpClient { graffiti: Option<&Graffiti>, skip_randao_verification: SkipRandaoVerification, builder_booster_factor: Option, + graffiti_policy: GraffitiPolicy, ) -> Result<(JsonProduceBlockV3Response, ProduceBlockV3Metadata), Error> { let path = self .get_validator_blocks_v3_path( @@ -2241,6 +2257,7 @@ impl BeaconNodeHttpClient { graffiti, skip_randao_verification, builder_booster_factor, + graffiti_policy, ) .await?; @@ -2283,6 +2300,7 @@ impl BeaconNodeHttpClient { randao_reveal: &SignatureBytes, graffiti: Option<&Graffiti>, builder_booster_factor: Option, + graffiti_policy: GraffitiPolicy, ) -> Result<(ProduceBlockV3Response, ProduceBlockV3Metadata), Error> { self.get_validator_blocks_v3_modular_ssz::( slot, @@ -2290,6 +2308,7 @@ impl BeaconNodeHttpClient { graffiti, SkipRandaoVerification::No, builder_booster_factor, + graffiti_policy, ) .await } @@ -2302,6 +2321,7 @@ impl BeaconNodeHttpClient { graffiti: Option<&Graffiti>, skip_randao_verification: SkipRandaoVerification, builder_booster_factor: Option, + graffiti_policy: GraffitiPolicy, ) -> Result<(ProduceBlockV3Response, ProduceBlockV3Metadata), Error> { let path = self .get_validator_blocks_v3_path( @@ -2310,6 +2330,7 @@ impl BeaconNodeHttpClient { graffiti, skip_randao_verification, builder_booster_factor, + graffiti_policy, ) .await?; diff --git a/common/eth2/src/types.rs b/common/eth2/src/types.rs index b8c74d4dcdc..3f371b822ba 100644 --- a/common/eth2/src/types.rs +++ b/common/eth2/src/types.rs @@ -751,12 +751,21 @@ pub struct ProposerData { pub slot: Slot, } +#[derive(Clone, Copy, Serialize, Deserialize, Default)] +pub enum GraffitiPolicy { + #[default] + PreserveUserGraffiti, + AppendClientVersions, +} + #[derive(Clone, Deserialize)] pub struct ValidatorBlocksQuery { pub randao_reveal: SignatureBytes, pub graffiti: Option, pub skip_randao_verification: SkipRandaoVerification, pub builder_boost_factor: Option, + #[serde(default)] + pub graffiti_client: GraffitiPolicy, } #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Deserialize)] diff --git a/validator_client/src/config.rs b/validator_client/src/config.rs index c238de3be29..827aee8f360 100644 --- a/validator_client/src/config.rs +++ b/validator_client/src/config.rs @@ -232,8 +232,6 @@ impl Config { } if let Some(input_graffiti) = validator_client_config.graffiti_client.as_ref() { - let client_version = get_engine_version(1); - let string = "testing"; let combined = format!("{} {}", string, input_graffiti); diff --git a/validator_client/validator_services/src/block_service.rs b/validator_client/validator_services/src/block_service.rs index 01f786e160b..151009cab42 100644 --- a/validator_client/validator_services/src/block_service.rs +++ b/validator_client/validator_services/src/block_service.rs @@ -1,5 +1,6 @@ use beacon_node_fallback::{ApiTopic, BeaconNodeFallback, Error as FallbackError, Errors}; use bls::SignatureBytes; +use eth2::types::GraffitiPolicy; use eth2::{BeaconNodeHttpClient, StatusCode}; use graffiti_file::{determine_graffiti, GraffitiFile}; use logging::crit; @@ -50,6 +51,7 @@ pub struct BlockServiceBuilder { chain_spec: Option>, graffiti: Option, graffiti_file: Option, + graffiti_policy: GraffitiPolicy, } impl BlockServiceBuilder { @@ -63,6 +65,7 @@ impl BlockServiceBuilder { chain_spec: None, graffiti: None, graffiti_file: None, + graffiti_policy: GraffitiPolicy::PreserveUserGraffiti, } } @@ -106,6 +109,11 @@ impl BlockServiceBuilder { self } + pub fn graffiti_policy(mut self, graffiti_policy: GraffitiPolicy) -> Self { + self.graffiti_policy = graffiti_policy; + self + } + pub fn build(self) -> Result, String> { Ok(BlockService { inner: Arc::new(Inner { @@ -127,6 +135,7 @@ impl BlockServiceBuilder { proposer_nodes: self.proposer_nodes, graffiti: self.graffiti, graffiti_file: self.graffiti_file, + graffiti_policy: self.graffiti_policy, }), }) } @@ -193,6 +202,7 @@ pub struct Inner { chain_spec: Arc, graffiti: Option, graffiti_file: Option, + graffiti_policy: GraffitiPolicy, } /// Attempts to produce attestations for any block producer(s) at the start of the epoch. @@ -460,6 +470,7 @@ impl BlockService { graffiti, proposer_index, builder_boost_factor, + self_ref.graffiti_policy, ) .await .map_err(|e| { @@ -523,6 +534,7 @@ impl BlockService { graffiti: Option, proposer_index: Option, builder_boost_factor: Option, + graffiti_policy: GraffitiPolicy, ) -> Result, BlockError> { let (block_response, _) = beacon_node .get_validator_blocks_v3::( @@ -530,6 +542,7 @@ impl BlockService { randao_reveal_ref, graffiti.as_ref(), builder_boost_factor, + graffiti_policy, ) .await .map_err(|e| { From c879763fbbdc4ce8ed9406a41820bf377c15f14a Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Wed, 4 Jun 2025 11:09:51 +0800 Subject: [PATCH 03/42] add server side --- beacon_node/beacon_chain/src/beacon_chain.rs | 5 +++++ beacon_node/http_api/src/produce_block.rs | 14 +++++++++++++- common/eth2/src/lib.rs | 17 ++++------------- common/eth2/src/types.rs | 13 +++++++++++-- 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index c1d30253a36..ca01b501924 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -7169,6 +7169,11 @@ impl BeaconChain { roots.reverse(); roots } + + // Add a public method to BeaconChain so we can access graffiti_calculator from another crate + pub fn calculate_graffiti(&self) -> &GraffitiCalculator { + &self.graffiti_calculator + } } impl Drop for BeaconChain { diff --git a/beacon_node/http_api/src/produce_block.rs b/beacon_node/http_api/src/produce_block.rs index db82ff214c8..97923a89b9d 100644 --- a/beacon_node/http_api/src/produce_block.rs +++ b/beacon_node/http_api/src/produce_block.rs @@ -10,7 +10,9 @@ use crate::{ use beacon_chain::{ BeaconBlockResponseWrapper, BeaconChain, BeaconChainTypes, ProduceBlockVerification, }; -use eth2::types::{self as api_types, ProduceBlockV3Metadata, SkipRandaoVerification}; +use eth2::types::{ + self as api_types, GraffitiPolicy, ProduceBlockV3Metadata, SkipRandaoVerification, +}; use ssz::Encode; use std::sync::Arc; use types::{payload::BlockProductionVersion, *}; @@ -61,6 +63,16 @@ pub async fn produce_block_v3( query.builder_boost_factor }; + let _final_graffiti = match query.graffiti_policy { + GraffitiPolicy::PreserveUserGraffiti => query.graffiti, + GraffitiPolicy::AppendClientVersions => Some( + chain + .calculate_graffiti() + .get_graffiti(query.graffiti) + .await, + ), + }; + let block_response_type = chain .produce_block_with_verification( randao_reveal, diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index a708cc9f117..db7a62029c4 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -2177,7 +2177,7 @@ impl BeaconNodeHttpClient { graffiti: Option<&Graffiti>, skip_randao_verification: SkipRandaoVerification, builder_booster_factor: Option, - graffiti_policy: GraffitiPolicy, // or ValidatorBlocksQuery + graffiti_policy: GraffitiPolicy, ) -> Result { let mut path = self.eth_path(V3)?; @@ -2205,17 +2205,8 @@ impl BeaconNodeHttpClient { .append_pair("builder_boost_factor", &builder_booster_factor.to_string()); } - // Add graffiti policy - match graffiti_policy { - GraffitiPolicy::AppendClientVersions => { - path.query_pairs_mut() - .append_pair("graffity_policy", "append_client_versions"); - } - GraffitiPolicy::PreserveUserGraffiti => { - path.query_pairs_mut() - .append_pair("graffiti_policy", "preserve_user_graffiti"); - } - } + path.query_pairs_mut() + .append_pair("graffiti_policy", &graffiti_policy.to_string()); Ok(path) } @@ -2227,7 +2218,7 @@ impl BeaconNodeHttpClient { randao_reveal: &SignatureBytes, graffiti: Option<&Graffiti>, builder_booster_factor: Option, - graffiti_policy: GraffitiPolicy, // add graffitipolicy validator + graffiti_policy: GraffitiPolicy, ) -> Result<(JsonProduceBlockV3Response, ProduceBlockV3Metadata), Error> { self.get_validator_blocks_v3_modular( slot, diff --git a/common/eth2/src/types.rs b/common/eth2/src/types.rs index 3f371b822ba..bf03b93bba5 100644 --- a/common/eth2/src/types.rs +++ b/common/eth2/src/types.rs @@ -13,7 +13,7 @@ use serde::{Deserialize, Deserializer, Serialize}; use serde_utils::quoted_u64::Quoted; use ssz::{Decode, DecodeError}; use ssz_derive::{Decode, Encode}; -use std::fmt::{self, Display}; +use std::fmt::{self, Display, Formatter}; use std::str::FromStr; use std::sync::Arc; use std::time::Duration; @@ -758,6 +758,15 @@ pub enum GraffitiPolicy { AppendClientVersions, } +impl fmt::Display for GraffitiPolicy { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + GraffitiPolicy::PreserveUserGraffiti => write!(f, "preserve_user_graffiti"), + GraffitiPolicy::AppendClientVersions => write!(f, "append_client_versions"), + } + } +} + #[derive(Clone, Deserialize)] pub struct ValidatorBlocksQuery { pub randao_reveal: SignatureBytes, @@ -765,7 +774,7 @@ pub struct ValidatorBlocksQuery { pub skip_randao_verification: SkipRandaoVerification, pub builder_boost_factor: Option, #[serde(default)] - pub graffiti_client: GraffitiPolicy, + pub graffiti_policy: GraffitiPolicy, } #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Deserialize)] From 054a56dcef8a4b46e1512fea236809a5aa1e9809 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Wed, 4 Jun 2025 11:12:12 +0800 Subject: [PATCH 04/42] add Option --- common/eth2/src/lib.rs | 16 +++++++++------- .../validator_services/src/block_service.rs | 4 ++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index db7a62029c4..1d746cf491d 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -2177,7 +2177,7 @@ impl BeaconNodeHttpClient { graffiti: Option<&Graffiti>, skip_randao_verification: SkipRandaoVerification, builder_booster_factor: Option, - graffiti_policy: GraffitiPolicy, + graffiti_policy: Option, ) -> Result { let mut path = self.eth_path(V3)?; @@ -2205,8 +2205,10 @@ impl BeaconNodeHttpClient { .append_pair("builder_boost_factor", &builder_booster_factor.to_string()); } - path.query_pairs_mut() - .append_pair("graffiti_policy", &graffiti_policy.to_string()); + if let Some(graffiti_policy) = graffiti_policy { + path.query_pairs_mut() + .append_pair("graffiti_policy", &graffiti_policy.to_string()); + } Ok(path) } @@ -2218,7 +2220,7 @@ impl BeaconNodeHttpClient { randao_reveal: &SignatureBytes, graffiti: Option<&Graffiti>, builder_booster_factor: Option, - graffiti_policy: GraffitiPolicy, + graffiti_policy: Option, ) -> Result<(JsonProduceBlockV3Response, ProduceBlockV3Metadata), Error> { self.get_validator_blocks_v3_modular( slot, @@ -2239,7 +2241,7 @@ impl BeaconNodeHttpClient { graffiti: Option<&Graffiti>, skip_randao_verification: SkipRandaoVerification, builder_booster_factor: Option, - graffiti_policy: GraffitiPolicy, + graffiti_policy: Option, ) -> Result<(JsonProduceBlockV3Response, ProduceBlockV3Metadata), Error> { let path = self .get_validator_blocks_v3_path( @@ -2291,7 +2293,7 @@ impl BeaconNodeHttpClient { randao_reveal: &SignatureBytes, graffiti: Option<&Graffiti>, builder_booster_factor: Option, - graffiti_policy: GraffitiPolicy, + graffiti_policy: Option, ) -> Result<(ProduceBlockV3Response, ProduceBlockV3Metadata), Error> { self.get_validator_blocks_v3_modular_ssz::( slot, @@ -2312,7 +2314,7 @@ impl BeaconNodeHttpClient { graffiti: Option<&Graffiti>, skip_randao_verification: SkipRandaoVerification, builder_booster_factor: Option, - graffiti_policy: GraffitiPolicy, + graffiti_policy: Option, ) -> Result<(ProduceBlockV3Response, ProduceBlockV3Metadata), Error> { let path = self .get_validator_blocks_v3_path( diff --git a/validator_client/validator_services/src/block_service.rs b/validator_client/validator_services/src/block_service.rs index 151009cab42..8cbd3a9c999 100644 --- a/validator_client/validator_services/src/block_service.rs +++ b/validator_client/validator_services/src/block_service.rs @@ -470,7 +470,7 @@ impl BlockService { graffiti, proposer_index, builder_boost_factor, - self_ref.graffiti_policy, + Some(self_ref.graffiti_policy), ) .await .map_err(|e| { @@ -534,7 +534,7 @@ impl BlockService { graffiti: Option, proposer_index: Option, builder_boost_factor: Option, - graffiti_policy: GraffitiPolicy, + graffiti_policy: Option, ) -> Result, BlockError> { let (block_response, _) = beacon_node .get_validator_blocks_v3::( From 9f5920826d0789ff9c19eb986d79468bcbcfc87f Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Wed, 4 Jun 2025 12:03:11 +0800 Subject: [PATCH 05/42] implement flag and config --- common/eth2/src/types.rs | 2 +- validator_client/src/config.rs | 29 ++++++------------- .../validator_services/src/block_service.rs | 10 +++---- 3 files changed, 15 insertions(+), 26 deletions(-) diff --git a/common/eth2/src/types.rs b/common/eth2/src/types.rs index bf03b93bba5..dddbe1d02dd 100644 --- a/common/eth2/src/types.rs +++ b/common/eth2/src/types.rs @@ -751,7 +751,7 @@ pub struct ProposerData { pub slot: Slot, } -#[derive(Clone, Copy, Serialize, Deserialize, Default)] +#[derive(Clone, Copy, Serialize, Deserialize, Default, Debug)] pub enum GraffitiPolicy { #[default] PreserveUserGraffiti, diff --git a/validator_client/src/config.rs b/validator_client/src/config.rs index 827aee8f360..0e09fd8eeaa 100644 --- a/validator_client/src/config.rs +++ b/validator_client/src/config.rs @@ -7,7 +7,7 @@ use directory::{ get_network_dir, DEFAULT_HARDCODED_NETWORK, DEFAULT_ROOT_DIR, DEFAULT_SECRET_DIR, DEFAULT_VALIDATOR_DIR, }; -use eth2::types::Graffiti; +use eth2::types::{Graffiti, GraffitiPolicy}; use graffiti_file::GraffitiFile; use initialized_validators::Config as InitializedValidatorsConfig; use lighthouse_validator_store::Config as ValidatorStoreConfig; @@ -55,6 +55,8 @@ pub struct Config { pub graffiti: Option, /// Graffiti file to load per validator graffitis. pub graffiti_file: Option, + /// GraffitiPolicy to append client version info + pub graffiti_policy: Option, /// Configuration for the HTTP REST API. pub http_api: validator_http_api::Config, /// Configuration for the HTTP REST API. @@ -117,6 +119,7 @@ impl Default for Config { long_timeouts_multiplier: 1, graffiti: None, graffiti_file: None, + graffiti_policy: None, http_api: <_>::default(), http_metrics: <_>::default(), beacon_node_fallback: <_>::default(), @@ -231,25 +234,11 @@ impl Config { } } - if let Some(input_graffiti) = validator_client_config.graffiti_client.as_ref() { - let string = "testing"; - - let combined = format!("{} {}", string, input_graffiti); - - let combined_bytes = combined.as_bytes(); - - if combined.len() > GRAFFITI_BYTES_LEN { - return Err(format!( - "Your graffiti is too long! Some bytes are reserved for client version. {} bytes maximum!", - GRAFFITI_BYTES_LEN - string.len() - )); - } else { - let mut graffiti = [0; 32]; - graffiti[..combined.len()].copy_from_slice(combined_bytes); - - config.graffiti = Some(graffiti.into()); - } - } + config.graffiti_policy = if validator_client_config.graffiti_client.is_some() { + Some(GraffitiPolicy::AppendClientVersions) + } else { + Some(GraffitiPolicy::PreserveUserGraffiti) + }; if let Some(input_fee_recipient) = validator_client_config.suggested_fee_recipient { config.validator_store.fee_recipient = Some(input_fee_recipient); diff --git a/validator_client/validator_services/src/block_service.rs b/validator_client/validator_services/src/block_service.rs index 8cbd3a9c999..aadbf294db1 100644 --- a/validator_client/validator_services/src/block_service.rs +++ b/validator_client/validator_services/src/block_service.rs @@ -51,7 +51,7 @@ pub struct BlockServiceBuilder { chain_spec: Option>, graffiti: Option, graffiti_file: Option, - graffiti_policy: GraffitiPolicy, + graffiti_policy: Option, } impl BlockServiceBuilder { @@ -65,7 +65,7 @@ impl BlockServiceBuilder { chain_spec: None, graffiti: None, graffiti_file: None, - graffiti_policy: GraffitiPolicy::PreserveUserGraffiti, + graffiti_policy: None, } } @@ -109,7 +109,7 @@ impl BlockServiceBuilder { self } - pub fn graffiti_policy(mut self, graffiti_policy: GraffitiPolicy) -> Self { + pub fn graffiti_policy(mut self, graffiti_policy: Option) -> Self { self.graffiti_policy = graffiti_policy; self } @@ -202,7 +202,7 @@ pub struct Inner { chain_spec: Arc, graffiti: Option, graffiti_file: Option, - graffiti_policy: GraffitiPolicy, + graffiti_policy: Option, } /// Attempts to produce attestations for any block producer(s) at the start of the epoch. @@ -470,7 +470,7 @@ impl BlockService { graffiti, proposer_index, builder_boost_factor, - Some(self_ref.graffiti_policy), + self_ref.graffiti_policy, ) .await .map_err(|e| { From 7d18f7f0cc273fb3a683fe483978a216b25aa242 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Wed, 4 Jun 2025 20:44:20 +0800 Subject: [PATCH 06/42] final graffiti --- beacon_node/http_api/src/produce_block.rs | 4 ++-- validator_client/src/cli.rs | 2 +- validator_client/src/config.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/beacon_node/http_api/src/produce_block.rs b/beacon_node/http_api/src/produce_block.rs index 97923a89b9d..09cb3c9f5ad 100644 --- a/beacon_node/http_api/src/produce_block.rs +++ b/beacon_node/http_api/src/produce_block.rs @@ -63,7 +63,7 @@ pub async fn produce_block_v3( query.builder_boost_factor }; - let _final_graffiti = match query.graffiti_policy { + let final_graffiti = match query.graffiti_policy { GraffitiPolicy::PreserveUserGraffiti => query.graffiti, GraffitiPolicy::AppendClientVersions => Some( chain @@ -77,7 +77,7 @@ pub async fn produce_block_v3( .produce_block_with_verification( randao_reveal, slot, - query.graffiti, + final_graffiti, randao_verification, builder_boost_factor, BlockProductionVersion::V3, diff --git a/validator_client/src/cli.rs b/validator_client/src/cli.rs index f05fbb8a2a8..b1f98319a9c 100644 --- a/validator_client/src/cli.rs +++ b/validator_client/src/cli.rs @@ -158,7 +158,7 @@ pub struct ValidatorClient { help = "When used, client version info will be automatically appended to user custom graffiti.", display_order = 0 )] - pub graffiti_client: Option, + pub graffiti_client: bool, #[clap( long, diff --git a/validator_client/src/config.rs b/validator_client/src/config.rs index 0e09fd8eeaa..14f9b6c975a 100644 --- a/validator_client/src/config.rs +++ b/validator_client/src/config.rs @@ -234,7 +234,7 @@ impl Config { } } - config.graffiti_policy = if validator_client_config.graffiti_client.is_some() { + config.graffiti_policy = if validator_client_config.graffiti_client { Some(GraffitiPolicy::AppendClientVersions) } else { Some(GraffitiPolicy::PreserveUserGraffiti) From 060b7bf07295095ab5dfcba71bf80deb989b9af3 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Wed, 4 Jun 2025 20:54:44 +0800 Subject: [PATCH 07/42] cli --- validator_client/src/cli.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator_client/src/cli.rs b/validator_client/src/cli.rs index b1f98319a9c..d0b1f638f1c 100644 --- a/validator_client/src/cli.rs +++ b/validator_client/src/cli.rs @@ -154,7 +154,7 @@ pub struct ValidatorClient { #[clap( long, value_name = "GRAFFITI", - conflicts_with = "graffiti", + requires = "graffiti", help = "When used, client version info will be automatically appended to user custom graffiti.", display_order = 0 )] From a14e7d30e071ef707bc3bb0f2db9eb6422208efe Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Tue, 10 Jun 2025 13:48:31 +0800 Subject: [PATCH 08/42] graffiti settings --- beacon_node/beacon_chain/src/beacon_chain.rs | 10 ++-- .../beacon_chain/src/graffiti_calculator.rs | 53 +++++++++++++++++-- beacon_node/beacon_chain/src/test_utils.rs | 11 ++-- beacon_node/http_api/src/produce_block.rs | 24 ++++----- 4 files changed, 70 insertions(+), 28 deletions(-) diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index ca01b501924..3b8d0a6c888 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -33,7 +33,7 @@ use crate::events::ServerSentEventHandler; use crate::execution_payload::{get_execution_payload, NotifyExecutionLayer, PreparePayloadHandle}; use crate::fetch_blobs::EngineGetBlobsOutput; use crate::fork_choice_signal::{ForkChoiceSignalRx, ForkChoiceSignalTx, ForkChoiceWaitResult}; -use crate::graffiti_calculator::GraffitiCalculator; +use crate::graffiti_calculator::{GraffitiCalculator, GraffitiSettings}; use crate::kzg_utils::reconstruct_blobs; use crate::light_client_finality_update_verification::{ Error as LightClientFinalityUpdateError, VerifiedLightClientFinalityUpdate, @@ -4538,7 +4538,7 @@ impl BeaconChain { self: &Arc, randao_reveal: Signature, slot: Slot, - validator_graffiti: Option, + graffiti_settings: GraffitiSettings, verification: ProduceBlockVerification, builder_boost_factor: Option, block_production_version: BlockProductionVersion, @@ -4567,7 +4567,7 @@ impl BeaconChain { state_root_opt, slot, randao_reveal, - validator_graffiti, + graffiti_settings, verification, builder_boost_factor, block_production_version, @@ -5099,7 +5099,7 @@ impl BeaconChain { state_root_opt: Option, produce_at_slot: Slot, randao_reveal: Signature, - validator_graffiti: Option, + graffiti_settings: GraffitiSettings, verification: ProduceBlockVerification, builder_boost_factor: Option, block_production_version: BlockProductionVersion, @@ -5110,7 +5110,7 @@ impl BeaconChain { let chain = self.clone(); let graffiti = self .graffiti_calculator - .get_graffiti(validator_graffiti) + .get_graffiti(graffiti_settings) .await; let mut partial_beacon_block = self .task_executor diff --git a/beacon_node/beacon_chain/src/graffiti_calculator.rs b/beacon_node/beacon_chain/src/graffiti_calculator.rs index 23d1d69b1ca..31f0cefc95a 100644 --- a/beacon_node/beacon_chain/src/graffiti_calculator.rs +++ b/beacon_node/beacon_chain/src/graffiti_calculator.rs @@ -1,5 +1,6 @@ use crate::BeaconChain; use crate::BeaconChainTypes; +use eth2::types::GraffitiPolicy; use execution_layer::{http::ENGINE_GET_CLIENT_VERSION_V1, CommitPrefix, ExecutionLayer}; use logging::crit; use serde::{Deserialize, Serialize}; @@ -48,6 +49,22 @@ impl Debug for GraffitiOrigin { } } +pub enum GraffitiSettings { + Unspecified, + Specified { + graffiti: Graffiti, + policy: GraffitiPolicy, + }, +} + +impl GraffitiSettings { + pub fn new(validator_graffiti: Option, policy: GraffitiPolicy) -> Self { + validator_graffiti + .map(|graffiti| Self::Specified { graffiti, policy }) + .unwrap_or(Self::Unspecified) + } +} + pub struct GraffitiCalculator { pub beacon_graffiti: GraffitiOrigin, execution_layer: Option>, @@ -73,7 +90,19 @@ impl GraffitiCalculator { /// 2. Graffiti specified by the user via beacon node CLI options. /// 3. The EL & CL client version string, applicable when the EL supports version specification. /// 4. The default lighthouse version string, used if the EL lacks version specification support. - pub async fn get_graffiti(&self, validator_graffiti: Option) -> Graffiti { + pub async fn get_graffiti(&self, graffiti_settings: GraffitiSettings) -> Graffiti { + match graffiti_settings { + GraffitiSettings::Specified { graffiti, policy } => match policy { + GraffitiPolicy::PreserveUserGraffiti => graffiti, + GraffitiPolicy::AppendClientVersions => { + self.calculate_combined_graffiti(Some(graffiti)).await + } + }, + GraffitiSettings::Unspecified => self.calculate_combined_graffiti(None).await, + } + } + + async fn calculate_combined_graffiti(&self, validator_graffiti: Option) -> Graffiti { if let Some(graffiti) = validator_graffiti { return graffiti; } @@ -221,8 +250,10 @@ async fn engine_version_cache_refresh_service( #[cfg(test)] mod tests { + use crate::graffiti_calculator::GraffitiSettings; use crate::test_utils::{test_spec, BeaconChainHarness, EphemeralHarnessType}; use crate::ChainConfig; + use eth2::types::GraffitiPolicy; use execution_layer::test_utils::{DEFAULT_CLIENT_VERSION, DEFAULT_ENGINE_CAPABILITIES}; use execution_layer::EngineCapabilities; use std::sync::Arc; @@ -278,8 +309,12 @@ mod tests { let version_bytes = std::cmp::min(lighthouse_version::VERSION.len(), GRAFFITI_BYTES_LEN); // grab the slice of the graffiti that corresponds to the lighthouse version - let graffiti_slice = - &harness.chain.graffiti_calculator.get_graffiti(None).await.0[..version_bytes]; + let graffiti_slice = &harness + .chain + .graffiti_calculator + .get_graffiti(GraffitiSettings::Unspecified) + .await + .0[..version_bytes]; // convert graffiti bytes slice to ascii for easy debugging if this test should fail let graffiti_str = @@ -300,7 +335,12 @@ mod tests { let spec = Arc::new(test_spec::()); let harness = get_harness(VALIDATOR_COUNT, spec, None); - let found_graffiti_bytes = harness.chain.graffiti_calculator.get_graffiti(None).await.0; + let found_graffiti_bytes = harness + .chain + .graffiti_calculator + .get_graffiti(GraffitiSettings::Unspecified) + .await + .0; let mock_commit = DEFAULT_CLIENT_VERSION.commit.clone(); let expected_graffiti_string = format!( @@ -349,7 +389,10 @@ mod tests { let found_graffiti = harness .chain .graffiti_calculator - .get_graffiti(Some(Graffiti::from(graffiti_bytes))) + .get_graffiti(GraffitiSettings::new( + Some(Graffiti::from(graffiti_bytes)), + GraffitiPolicy::PreserveUserGraffiti, + )) .await; assert_eq!( diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index d3689f70680..4248fe6aa55 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -1,6 +1,7 @@ use crate::blob_verification::GossipVerifiedBlob; use crate::block_verification_types::{AsBlock, RpcBlock}; use crate::data_column_verification::CustodyDataColumn; +use crate::graffiti_calculator::GraffitiSettings; use crate::kzg_utils::build_data_column_sidecars; use crate::observed_operations::ObservationOutcome; pub use crate::persisted_beacon_chain::PersistedBeaconChain; @@ -20,7 +21,7 @@ use crate::{ }; use crate::{get_block_root, BeaconBlockResponseWrapper}; use bls::get_withdrawal_credentials; -use eth2::types::SignedBlockContentsTuple; +use eth2::types::{GraffitiPolicy, SignedBlockContentsTuple}; use execution_layer::test_utils::generate_genesis_header; use execution_layer::{ auth::JwtKey, @@ -942,6 +943,8 @@ where // BeaconChain errors out with `DuplicateFullyImported`. Vary the graffiti so that we produce // different blocks each time. let graffiti = Graffiti::from(self.rng.lock().gen::<[u8; 32]>()); + let graffiti_settings = + GraffitiSettings::new(Some(graffiti), GraffitiPolicy::PreserveUserGraffiti); let randao_reveal = self.sign_randao_reveal(&state, proposer_index, slot); @@ -952,7 +955,7 @@ where None, slot, randao_reveal, - Some(graffiti), + graffiti_settings, ProduceBlockVerification::VerifyRandao, None, BlockProductionVersion::FullV2, @@ -1001,6 +1004,8 @@ where // BeaconChain errors out with `DuplicateFullyImported`. Vary the graffiti so that we produce // different blocks each time. let graffiti = Graffiti::from(self.rng.lock().gen::<[u8; 32]>()); + let graffiti_settings = + GraffitiSettings::new(Some(graffiti), GraffitiPolicy::PreserveUserGraffiti); let randao_reveal = self.sign_randao_reveal(&state, proposer_index, slot); @@ -1013,7 +1018,7 @@ where None, slot, randao_reveal, - Some(graffiti), + graffiti_settings, ProduceBlockVerification::VerifyRandao, None, BlockProductionVersion::FullV2, diff --git a/beacon_node/http_api/src/produce_block.rs b/beacon_node/http_api/src/produce_block.rs index 09cb3c9f5ad..71d7866b6ed 100644 --- a/beacon_node/http_api/src/produce_block.rs +++ b/beacon_node/http_api/src/produce_block.rs @@ -7,12 +7,11 @@ use crate::{ ResponseIncludesVersion, }, }; +use beacon_chain::graffiti_calculator::GraffitiSettings; use beacon_chain::{ BeaconBlockResponseWrapper, BeaconChain, BeaconChainTypes, ProduceBlockVerification, }; -use eth2::types::{ - self as api_types, GraffitiPolicy, ProduceBlockV3Metadata, SkipRandaoVerification, -}; +use eth2::types::{self as api_types, ProduceBlockV3Metadata, SkipRandaoVerification}; use ssz::Encode; use std::sync::Arc; use types::{payload::BlockProductionVersion, *}; @@ -63,21 +62,13 @@ pub async fn produce_block_v3( query.builder_boost_factor }; - let final_graffiti = match query.graffiti_policy { - GraffitiPolicy::PreserveUserGraffiti => query.graffiti, - GraffitiPolicy::AppendClientVersions => Some( - chain - .calculate_graffiti() - .get_graffiti(query.graffiti) - .await, - ), - }; + let graffiti_settings = GraffitiSettings::new(query.graffiti, query.graffiti_policy); let block_response_type = chain .produce_block_with_verification( randao_reveal, slot, - final_graffiti, + graffiti_settings, randao_verification, builder_boost_factor, BlockProductionVersion::V3, @@ -153,11 +144,13 @@ pub async fn produce_blinded_block_v2( })?; let randao_verification = get_randao_verification(&query, randao_reveal.is_infinity())?; + let graffiti_settings = GraffitiSettings::new(query.graffiti, query.graffiti_policy); + let block_response_type = chain .produce_block_with_verification( randao_reveal, slot, - query.graffiti, + graffiti_settings, randao_verification, None, BlockProductionVersion::BlindedV2, @@ -182,12 +175,13 @@ pub async fn produce_block_v2( })?; let randao_verification = get_randao_verification(&query, randao_reveal.is_infinity())?; + let graffiti_settings = GraffitiSettings::new(query.graffiti, query.graffiti_policy); let block_response_type = chain .produce_block_with_verification( randao_reveal, slot, - query.graffiti, + graffiti_settings, randao_verification, None, BlockProductionVersion::FullV2, From 7cf8b118cc782db1623257b25b74060a99b76b2f Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Wed, 11 Jun 2025 14:59:47 +0800 Subject: [PATCH 09/42] cli --- validator_client/src/cli.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validator_client/src/cli.rs b/validator_client/src/cli.rs index d0b1f638f1c..3e6f3930fbd 100644 --- a/validator_client/src/cli.rs +++ b/validator_client/src/cli.rs @@ -153,10 +153,10 @@ pub struct ValidatorClient { #[clap( long, - value_name = "GRAFFITI", requires = "graffiti", help = "When used, client version info will be automatically appended to user custom graffiti.", - display_order = 0 + display_order = 0, + help_heading = FLAG_HEADER )] pub graffiti_client: bool, From 4a336e989113f569111e32b48a8e98c0ecd009fc Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Wed, 11 Jun 2025 15:10:38 +0800 Subject: [PATCH 10/42] update cli --- validator_client/src/cli.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/validator_client/src/cli.rs b/validator_client/src/cli.rs index 3e6f3930fbd..dce4542e674 100644 --- a/validator_client/src/cli.rs +++ b/validator_client/src/cli.rs @@ -145,7 +145,6 @@ pub struct ValidatorClient { #[clap( long, value_name = "GRAFFITI", - conflicts_with = "graffiti_client", help = "Specify your custom graffiti to be included in blocks.", display_order = 0 )] From 6ad919ffbbe102e48a773009049619a9ae84a344 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Fri, 13 Jun 2025 10:37:31 +0800 Subject: [PATCH 11/42] pass graffiti policy to service builder --- validator_client/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/validator_client/src/lib.rs b/validator_client/src/lib.rs index 6692fe3a7b6..57cc2490bf7 100644 --- a/validator_client/src/lib.rs +++ b/validator_client/src/lib.rs @@ -467,7 +467,8 @@ impl ProductionValidatorClient { .executor(context.executor.clone()) .chain_spec(context.eth2_config.spec.clone()) .graffiti(config.graffiti) - .graffiti_file(config.graffiti_file.clone()); + .graffiti_file(config.graffiti_file.clone()) + .graffiti_policy(config.graffiti_policy); // If we have proposer nodes, add them to the block service builder. if proposer_nodes_num > 0 { From c0c6efa9afa1da7d5c83fa1926489f5354896410 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Fri, 13 Jun 2025 14:12:14 +0800 Subject: [PATCH 12/42] add log --- common/eth2/src/types.rs | 1 - validator_client/validator_services/src/block_service.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/common/eth2/src/types.rs b/common/eth2/src/types.rs index dddbe1d02dd..2c04cb26d25 100644 --- a/common/eth2/src/types.rs +++ b/common/eth2/src/types.rs @@ -773,7 +773,6 @@ pub struct ValidatorBlocksQuery { pub graffiti: Option, pub skip_randao_verification: SkipRandaoVerification, pub builder_boost_factor: Option, - #[serde(default)] pub graffiti_policy: GraffitiPolicy, } diff --git a/validator_client/validator_services/src/block_service.rs b/validator_client/validator_services/src/block_service.rs index aadbf294db1..6701e1e61de 100644 --- a/validator_client/validator_services/src/block_service.rs +++ b/validator_client/validator_services/src/block_service.rs @@ -452,7 +452,7 @@ impl BlockService { }; info!(slot = slot.as_u64(), "Requesting unsigned block"); - + info!(self.graffiti_policy, "Graffiti policy is"); // Request block from first responsive beacon node. // // Try the proposer nodes last, since it's likely that they don't have a From 9035834e412cf75268eb9a88ff90237570b2daef Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Fri, 13 Jun 2025 15:47:53 +0800 Subject: [PATCH 13/42] fix logging --- validator_client/validator_services/src/block_service.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator_client/validator_services/src/block_service.rs b/validator_client/validator_services/src/block_service.rs index 6701e1e61de..a7b5b61779b 100644 --- a/validator_client/validator_services/src/block_service.rs +++ b/validator_client/validator_services/src/block_service.rs @@ -452,7 +452,7 @@ impl BlockService { }; info!(slot = slot.as_u64(), "Requesting unsigned block"); - info!(self.graffiti_policy, "Graffiti policy is"); + info!(?self.graffiti_policy, "Graffiti policy is"); // Request block from first responsive beacon node. // // Try the proposer nodes last, since it's likely that they don't have a From efe855d7687ee37c45301c06851d64ffbe9fcff5 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Mon, 16 Jun 2025 10:37:19 +0800 Subject: [PATCH 14/42] add Some() to graffiti policy --- beacon_node/beacon_chain/src/graffiti_calculator.rs | 9 ++++++--- beacon_node/beacon_chain/src/test_utils.rs | 4 ++-- common/eth2/src/types.rs | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/beacon_node/beacon_chain/src/graffiti_calculator.rs b/beacon_node/beacon_chain/src/graffiti_calculator.rs index 31f0cefc95a..4bded93b2e2 100644 --- a/beacon_node/beacon_chain/src/graffiti_calculator.rs +++ b/beacon_node/beacon_chain/src/graffiti_calculator.rs @@ -58,9 +58,12 @@ pub enum GraffitiSettings { } impl GraffitiSettings { - pub fn new(validator_graffiti: Option, policy: GraffitiPolicy) -> Self { + pub fn new(validator_graffiti: Option, policy: Option) -> Self { validator_graffiti - .map(|graffiti| Self::Specified { graffiti, policy }) + .map(|graffiti| Self::Specified { + graffiti, + policy: policy.unwrap_or(GraffitiPolicy::PreserveUserGraffiti), + }) .unwrap_or(Self::Unspecified) } } @@ -391,7 +394,7 @@ mod tests { .graffiti_calculator .get_graffiti(GraffitiSettings::new( Some(Graffiti::from(graffiti_bytes)), - GraffitiPolicy::PreserveUserGraffiti, + Some(GraffitiPolicy::PreserveUserGraffiti), )) .await; diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 4248fe6aa55..acdbefac520 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -944,7 +944,7 @@ where // different blocks each time. let graffiti = Graffiti::from(self.rng.lock().gen::<[u8; 32]>()); let graffiti_settings = - GraffitiSettings::new(Some(graffiti), GraffitiPolicy::PreserveUserGraffiti); + GraffitiSettings::new(Some(graffiti), Some(GraffitiPolicy::PreserveUserGraffiti)); let randao_reveal = self.sign_randao_reveal(&state, proposer_index, slot); @@ -1005,7 +1005,7 @@ where // different blocks each time. let graffiti = Graffiti::from(self.rng.lock().gen::<[u8; 32]>()); let graffiti_settings = - GraffitiSettings::new(Some(graffiti), GraffitiPolicy::PreserveUserGraffiti); + GraffitiSettings::new(Some(graffiti), Some(GraffitiPolicy::PreserveUserGraffiti)); let randao_reveal = self.sign_randao_reveal(&state, proposer_index, slot); diff --git a/common/eth2/src/types.rs b/common/eth2/src/types.rs index 2c04cb26d25..9e7a7b7c869 100644 --- a/common/eth2/src/types.rs +++ b/common/eth2/src/types.rs @@ -773,7 +773,7 @@ pub struct ValidatorBlocksQuery { pub graffiti: Option, pub skip_randao_verification: SkipRandaoVerification, pub builder_boost_factor: Option, - pub graffiti_policy: GraffitiPolicy, + pub graffiti_policy: Option, } #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Deserialize)] From 761f9bb63ab21b409eb0d1368da946a19533b3e8 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Mon, 16 Jun 2025 12:01:13 +0800 Subject: [PATCH 15/42] add url info log --- Cargo.lock | 1 + common/eth2/Cargo.toml | 1 + common/eth2/src/lib.rs | 2 ++ common/eth2/src/types.rs | 4 ++-- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30be5fa233a..0369ba91ad1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2742,6 +2742,7 @@ dependencies = [ "ssz_types", "test_random_derive", "tokio", + "tracing", "types", "zeroize", ] diff --git a/common/eth2/Cargo.toml b/common/eth2/Cargo.toml index 81666a64216..384f6f5049d 100644 --- a/common/eth2/Cargo.toml +++ b/common/eth2/Cargo.toml @@ -32,6 +32,7 @@ serde_json = { workspace = true } slashing_protection = { workspace = true } ssz_types = { workspace = true } test_random_derive = { path = "../../common/test_random_derive" } +tracing = { workspace = true } types = { workspace = true } zeroize = { workspace = true } diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index 1d746cf491d..4ce973673a7 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -37,6 +37,7 @@ use std::fmt; use std::future::Future; use std::path::PathBuf; use std::time::Duration; +use tracing::info; pub const V1: EndpointVersion = EndpointVersion(1); pub const V2: EndpointVersion = EndpointVersion(2); @@ -2209,6 +2210,7 @@ impl BeaconNodeHttpClient { path.query_pairs_mut() .append_pair("graffiti_policy", &graffiti_policy.to_string()); } + info!(url = %path, "Complete query url"); Ok(path) } diff --git a/common/eth2/src/types.rs b/common/eth2/src/types.rs index 9e7a7b7c869..00b5900ea18 100644 --- a/common/eth2/src/types.rs +++ b/common/eth2/src/types.rs @@ -761,8 +761,8 @@ pub enum GraffitiPolicy { impl fmt::Display for GraffitiPolicy { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - GraffitiPolicy::PreserveUserGraffiti => write!(f, "preserve_user_graffiti"), - GraffitiPolicy::AppendClientVersions => write!(f, "append_client_versions"), + GraffitiPolicy::PreserveUserGraffiti => write!(f, "preserve-user-graffiti"), + GraffitiPolicy::AppendClientVersions => write!(f, "append-client-versions"), } } } From fd89131fa378e4834c34a1b4d3008d1b4ca2e9c9 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Mon, 16 Jun 2025 13:36:30 +0800 Subject: [PATCH 16/42] revise GraffitiPolicy display --- common/eth2/src/types.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/eth2/src/types.rs b/common/eth2/src/types.rs index 00b5900ea18..d1ebd549d2b 100644 --- a/common/eth2/src/types.rs +++ b/common/eth2/src/types.rs @@ -761,8 +761,8 @@ pub enum GraffitiPolicy { impl fmt::Display for GraffitiPolicy { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - GraffitiPolicy::PreserveUserGraffiti => write!(f, "preserve-user-graffiti"), - GraffitiPolicy::AppendClientVersions => write!(f, "append-client-versions"), + GraffitiPolicy::PreserveUserGraffiti => write!(f, "PreserveUserGraffiti"), + GraffitiPolicy::AppendClientVersions => write!(f, "AppendClientVersions"), } } } From 27b9ae5f3fcf6a39ce5b03d6b2de5a85a56ff857 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Mon, 16 Jun 2025 18:33:40 +0800 Subject: [PATCH 17/42] only client version info --- beacon_node/beacon_chain/src/graffiti_calculator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beacon_node/beacon_chain/src/graffiti_calculator.rs b/beacon_node/beacon_chain/src/graffiti_calculator.rs index 4bded93b2e2..a52f17bb351 100644 --- a/beacon_node/beacon_chain/src/graffiti_calculator.rs +++ b/beacon_node/beacon_chain/src/graffiti_calculator.rs @@ -98,7 +98,7 @@ impl GraffitiCalculator { GraffitiSettings::Specified { graffiti, policy } => match policy { GraffitiPolicy::PreserveUserGraffiti => graffiti, GraffitiPolicy::AppendClientVersions => { - self.calculate_combined_graffiti(Some(graffiti)).await + self.calculate_combined_graffiti(None).await } }, GraffitiSettings::Unspecified => self.calculate_combined_graffiti(None).await, From 9f9d9dd9e326b670df44972f4a03a78ac32c6edc Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Tue, 17 Jun 2025 22:29:18 +0800 Subject: [PATCH 18/42] http url request --- common/eth2/src/lib.rs | 7 +++++-- common/eth2/src/types.rs | 11 +---------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index 4ce973673a7..5322ad4389b 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -2206,9 +2206,12 @@ impl BeaconNodeHttpClient { .append_pair("builder_boost_factor", &builder_booster_factor.to_string()); } - if let Some(graffiti_policy) = graffiti_policy { + // Only append the HTTP URL request if the graffiti_policy is to AppendClientVersions + // If PreserveUserGraffiti (default), then the HTTP URL request does not contain graffiti_policy + // so that the default case is compliant to the spec + if let Some(GraffitiPolicy::AppendClientVersions) = graffiti_policy { path.query_pairs_mut() - .append_pair("graffiti_policy", &graffiti_policy.to_string()); + .append_pair("graffiti_policy", "AppendClientVersions"); } info!(url = %path, "Complete query url"); diff --git a/common/eth2/src/types.rs b/common/eth2/src/types.rs index d1ebd549d2b..5d506ce3350 100644 --- a/common/eth2/src/types.rs +++ b/common/eth2/src/types.rs @@ -13,7 +13,7 @@ use serde::{Deserialize, Deserializer, Serialize}; use serde_utils::quoted_u64::Quoted; use ssz::{Decode, DecodeError}; use ssz_derive::{Decode, Encode}; -use std::fmt::{self, Display, Formatter}; +use std::fmt::{self, Display}; use std::str::FromStr; use std::sync::Arc; use std::time::Duration; @@ -758,15 +758,6 @@ pub enum GraffitiPolicy { AppendClientVersions, } -impl fmt::Display for GraffitiPolicy { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - GraffitiPolicy::PreserveUserGraffiti => write!(f, "PreserveUserGraffiti"), - GraffitiPolicy::AppendClientVersions => write!(f, "AppendClientVersions"), - } - } -} - #[derive(Clone, Deserialize)] pub struct ValidatorBlocksQuery { pub randao_reveal: SignatureBytes, From af8f01e74e71cf2c0473d8db4e90b13653b9f57e Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Wed, 18 Jun 2025 17:37:36 +0800 Subject: [PATCH 19/42] append graffiti --- .../beacon_chain/src/graffiti_calculator.rs | 3 +- beacon_node/execution_layer/src/engine_api.rs | 36 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/beacon_node/beacon_chain/src/graffiti_calculator.rs b/beacon_node/beacon_chain/src/graffiti_calculator.rs index a52f17bb351..8fee8b6607d 100644 --- a/beacon_node/beacon_chain/src/graffiti_calculator.rs +++ b/beacon_node/beacon_chain/src/graffiti_calculator.rs @@ -163,7 +163,8 @@ impl GraffitiCalculator { CommitPrefix("00000000".to_string()) }); - engine_version.calculate_graffiti(lighthouse_commit_prefix) + engine_version + .calculate_graffiti_append(lighthouse_commit_prefix, validator_graffiti) } } } diff --git a/beacon_node/execution_layer/src/engine_api.rs b/beacon_node/execution_layer/src/engine_api.rs index 4bfee223ff4..bd4b24980e5 100644 --- a/beacon_node/execution_layer/src/engine_api.rs +++ b/beacon_node/execution_layer/src/engine_api.rs @@ -751,4 +751,40 @@ impl ClientVersionV1 { Graffiti::from(graffiti_bytes) } + + pub fn calculate_graffiti_append( + &self, + lighthouse_commit_prefix: CommitPrefix, + validator_graffiti: Option, + ) -> Graffiti { + let base_graffiti = format!( + "{}{}LH{}", + self.code, + self.commit + .0 + .get(..4) + .map_or_else(|| self.commit.0.as_str(), |s| s) + .to_lowercase(), + lighthouse_commit_prefix + .0 + .get(..4) + .unwrap_or("0000") + .to_lowercase(), + ); + + let graffiti_string = match validator_graffiti { + Some(graffiti) => { + let graffiti_str = graffiti.as_utf8_lossy(); + format!("{} {}", base_graffiti, graffiti_str) + } + None => base_graffiti, + }; + + let mut graffiti_bytes = [0u8; GRAFFITI_BYTES_LEN]; + let bytes_to_copy = std::cmp::min(graffiti_string.len(), GRAFFITI_BYTES_LEN); + graffiti_bytes[..bytes_to_copy] + .copy_from_slice(&graffiti_string.as_bytes()[..bytes_to_copy]); + + Graffiti::from(graffiti_bytes) + } } From 32cb2eac32c08760cc7b31ae75a7266b84c843bf Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Wed, 18 Jun 2025 21:07:32 +0800 Subject: [PATCH 20/42] append graffiti --- beacon_node/beacon_chain/src/graffiti_calculator.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/beacon_node/beacon_chain/src/graffiti_calculator.rs b/beacon_node/beacon_chain/src/graffiti_calculator.rs index 8fee8b6607d..6d3135429c1 100644 --- a/beacon_node/beacon_chain/src/graffiti_calculator.rs +++ b/beacon_node/beacon_chain/src/graffiti_calculator.rs @@ -98,7 +98,7 @@ impl GraffitiCalculator { GraffitiSettings::Specified { graffiti, policy } => match policy { GraffitiPolicy::PreserveUserGraffiti => graffiti, GraffitiPolicy::AppendClientVersions => { - self.calculate_combined_graffiti(None).await + self.calculate_combined_graffiti(Some(graffiti)).await } }, GraffitiSettings::Unspecified => self.calculate_combined_graffiti(None).await, @@ -106,10 +106,6 @@ impl GraffitiCalculator { } async fn calculate_combined_graffiti(&self, validator_graffiti: Option) -> Graffiti { - if let Some(graffiti) = validator_graffiti { - return graffiti; - } - match self.beacon_graffiti { GraffitiOrigin::UserSpecified(graffiti) => graffiti, GraffitiOrigin::Calculated(default_graffiti) => { From 11d7f9e9e3e87585d4367100e0a75a0c22770935 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Tue, 24 Jun 2025 10:01:28 +0800 Subject: [PATCH 21/42] remove public method calculate_graffiti --- beacon_node/beacon_chain/src/beacon_chain.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 3b8d0a6c888..9a453eb4254 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -7169,11 +7169,6 @@ impl BeaconChain { roots.reverse(); roots } - - // Add a public method to BeaconChain so we can access graffiti_calculator from another crate - pub fn calculate_graffiti(&self) -> &GraffitiCalculator { - &self.graffiti_calculator - } } impl Drop for BeaconChain { From 985cbcbbf6be1a0cf85cbb10de7890ec5040b96c Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Tue, 24 Jun 2025 10:50:03 +0800 Subject: [PATCH 22/42] add todo --- common/eth2/src/lib.rs | 1 + validator_client/validator_services/src/block_service.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index 5322ad4389b..80d21d29971 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -2213,6 +2213,7 @@ impl BeaconNodeHttpClient { path.query_pairs_mut() .append_pair("graffiti_policy", "AppendClientVersions"); } + // TODO, will be removed later info!(url = %path, "Complete query url"); Ok(path) diff --git a/validator_client/validator_services/src/block_service.rs b/validator_client/validator_services/src/block_service.rs index a7b5b61779b..960ae24854f 100644 --- a/validator_client/validator_services/src/block_service.rs +++ b/validator_client/validator_services/src/block_service.rs @@ -452,6 +452,7 @@ impl BlockService { }; info!(slot = slot.as_u64(), "Requesting unsigned block"); + // TODO, will be removed later info!(?self.graffiti_policy, "Graffiti policy is"); // Request block from first responsive beacon node. // From 8aa31b46d734e5389187096791a29ae6b0fe80ee Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Tue, 24 Jun 2025 11:02:05 +0800 Subject: [PATCH 23/42] simplify calcualte_graffiti function --- .../beacon_chain/src/graffiti_calculator.rs | 3 +-- beacon_node/execution_layer/src/engine_api.rs | 25 +------------------ 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/beacon_node/beacon_chain/src/graffiti_calculator.rs b/beacon_node/beacon_chain/src/graffiti_calculator.rs index 6d3135429c1..83b2ac61226 100644 --- a/beacon_node/beacon_chain/src/graffiti_calculator.rs +++ b/beacon_node/beacon_chain/src/graffiti_calculator.rs @@ -159,8 +159,7 @@ impl GraffitiCalculator { CommitPrefix("00000000".to_string()) }); - engine_version - .calculate_graffiti_append(lighthouse_commit_prefix, validator_graffiti) + engine_version.calculate_graffiti(lighthouse_commit_prefix, validator_graffiti) } } } diff --git a/beacon_node/execution_layer/src/engine_api.rs b/beacon_node/execution_layer/src/engine_api.rs index bd4b24980e5..9eda81760cd 100644 --- a/beacon_node/execution_layer/src/engine_api.rs +++ b/beacon_node/execution_layer/src/engine_api.rs @@ -729,30 +729,7 @@ pub struct ClientVersionV1 { } impl ClientVersionV1 { - pub fn calculate_graffiti(&self, lighthouse_commit_prefix: CommitPrefix) -> Graffiti { - let graffiti_string = format!( - "{}{}LH{}", - self.code, - self.commit - .0 - .get(..4) - .map_or_else(|| self.commit.0.as_str(), |s| s) - .to_lowercase(), - lighthouse_commit_prefix - .0 - .get(..4) - .unwrap_or("0000") - .to_lowercase(), - ); - let mut graffiti_bytes = [0u8; GRAFFITI_BYTES_LEN]; - let bytes_to_copy = std::cmp::min(graffiti_string.len(), GRAFFITI_BYTES_LEN); - graffiti_bytes[..bytes_to_copy] - .copy_from_slice(&graffiti_string.as_bytes()[..bytes_to_copy]); - - Graffiti::from(graffiti_bytes) - } - - pub fn calculate_graffiti_append( + pub fn calculate_graffiti( &self, lighthouse_commit_prefix: CommitPrefix, validator_graffiti: Option, From e5bb42e6066c3fe14a129a28c1e8ab370d5576f3 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Tue, 24 Jun 2025 11:13:46 +0800 Subject: [PATCH 24/42] make test compile --- .../http_api/tests/interactive_tests.rs | 2 +- beacon_node/http_api/tests/tests.rs | 44 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/beacon_node/http_api/tests/interactive_tests.rs b/beacon_node/http_api/tests/interactive_tests.rs index 4f3cd6c8285..ee93dec6451 100644 --- a/beacon_node/http_api/tests/interactive_tests.rs +++ b/beacon_node/http_api/tests/interactive_tests.rs @@ -633,7 +633,7 @@ pub async fn proposer_boost_re_org_test( .into(); let (unsigned_block_type, _) = tester .client - .get_validator_blocks_v3::(slot_c, &randao_reveal, None, None) + .get_validator_blocks_v3::(slot_c, &randao_reveal, None, None, None) .await .unwrap(); diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index a5a21fd985e..2c07cfed3c3 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -3329,7 +3329,7 @@ impl ApiTester { let (response, metadata) = self .client - .get_validator_blocks_v3_ssz::(slot, &randao_reveal, None, None) + .get_validator_blocks_v3_ssz::(slot, &randao_reveal, None, None, None) .await .unwrap(); @@ -4256,7 +4256,7 @@ impl ApiTester { let (payload_type, metadata) = self .client - .get_validator_blocks_v3::(slot, &randao_reveal, None, None) + .get_validator_blocks_v3::(slot, &randao_reveal, None, None, None) .await .unwrap(); Self::check_block_v3_metadata(&metadata, &payload_type); @@ -4283,7 +4283,7 @@ impl ApiTester { let (payload_type, metadata) = self .client - .get_validator_blocks_v3::(slot, &randao_reveal, None, Some(0)) + .get_validator_blocks_v3::(slot, &randao_reveal, None, Some(0), None) .await .unwrap(); Self::check_block_v3_metadata(&metadata, &payload_type); @@ -4311,7 +4311,7 @@ impl ApiTester { let (payload_type, metadata) = self .client - .get_validator_blocks_v3::(slot, &randao_reveal, None, Some(u64::MAX)) + .get_validator_blocks_v3::(slot, &randao_reveal, None, Some(u64::MAX), None) .await .unwrap(); Self::check_block_v3_metadata(&metadata, &payload_type); @@ -4465,7 +4465,7 @@ impl ApiTester { let (payload_type, metadata) = self .client - .get_validator_blocks_v3::(slot, &randao_reveal, None, None) + .get_validator_blocks_v3::(slot, &randao_reveal, None, None, None) .await .unwrap(); Self::check_block_v3_metadata(&metadata, &payload_type); @@ -4545,7 +4545,7 @@ impl ApiTester { let (payload_type, metadata) = self .client - .get_validator_blocks_v3::(slot, &randao_reveal, None, None) + .get_validator_blocks_v3::(slot, &randao_reveal, None, None, None) .await .unwrap(); Self::check_block_v3_metadata(&metadata, &payload_type); @@ -4639,7 +4639,7 @@ impl ApiTester { let (payload_type, metadata) = self .client - .get_validator_blocks_v3::(slot, &randao_reveal, None, None) + .get_validator_blocks_v3::(slot, &randao_reveal, None, None, None) .await .unwrap(); Self::check_block_v3_metadata(&metadata, &payload_type); @@ -4729,7 +4729,7 @@ impl ApiTester { let (payload_type, metadata) = self .client - .get_validator_blocks_v3::(slot, &randao_reveal, None, None) + .get_validator_blocks_v3::(slot, &randao_reveal, None, None, None) .await .unwrap(); Self::check_block_v3_metadata(&metadata, &payload_type); @@ -4819,7 +4819,7 @@ impl ApiTester { let (payload_type, metadata) = self .client - .get_validator_blocks_v3::(slot, &randao_reveal, None, None) + .get_validator_blocks_v3::(slot, &randao_reveal, None, None, None) .await .unwrap(); Self::check_block_v3_metadata(&metadata, &payload_type); @@ -4907,7 +4907,7 @@ impl ApiTester { let (payload_type, metadata) = self .client - .get_validator_blocks_v3::(slot, &randao_reveal, None, None) + .get_validator_blocks_v3::(slot, &randao_reveal, None, None, None) .await .unwrap(); Self::check_block_v3_metadata(&metadata, &payload_type); @@ -4967,7 +4967,7 @@ impl ApiTester { let (payload_type, metadata) = self .client - .get_validator_blocks_v3::(slot, &randao_reveal, None, None) + .get_validator_blocks_v3::(slot, &randao_reveal, None, None, None) .await .unwrap(); Self::check_block_v3_metadata(&metadata, &payload_type); @@ -5037,7 +5037,7 @@ impl ApiTester { let (payload_type, metadata) = self .client - .get_validator_blocks_v3::(slot, &randao_reveal, None, None) + .get_validator_blocks_v3::(slot, &randao_reveal, None, None, None) .await .unwrap(); Self::check_block_v3_metadata(&metadata, &payload_type); @@ -5150,7 +5150,7 @@ impl ApiTester { let (payload_type, metadata) = self .client - .get_validator_blocks_v3::(next_slot, &randao_reveal, None, None) + .get_validator_blocks_v3::(next_slot, &randao_reveal, None, None, None) .await .unwrap(); Self::check_block_v3_metadata(&metadata, &payload_type); @@ -5171,7 +5171,7 @@ impl ApiTester { let (payload_type, metadata) = self .client - .get_validator_blocks_v3::(next_slot, &randao_reveal, None, None) + .get_validator_blocks_v3::(next_slot, &randao_reveal, None, None, None) .await .unwrap(); Self::check_block_v3_metadata(&metadata, &payload_type); @@ -5304,7 +5304,7 @@ impl ApiTester { let (payload_type, metadata) = self .client - .get_validator_blocks_v3::(next_slot, &randao_reveal, None, None) + .get_validator_blocks_v3::(next_slot, &randao_reveal, None, None, None) .await .unwrap(); Self::check_block_v3_metadata(&metadata, &payload_type); @@ -5335,7 +5335,7 @@ impl ApiTester { let (payload_type, metadata) = self .client - .get_validator_blocks_v3::(next_slot, &randao_reveal, None, None) + .get_validator_blocks_v3::(next_slot, &randao_reveal, None, None, None) .await .unwrap(); Self::check_block_v3_metadata(&metadata, &payload_type); @@ -5416,7 +5416,7 @@ impl ApiTester { let (payload_type, metadata) = self .client - .get_validator_blocks_v3::(slot, &randao_reveal, None, None) + .get_validator_blocks_v3::(slot, &randao_reveal, None, None, None) .await .unwrap(); Self::check_block_v3_metadata(&metadata, &payload_type); @@ -5489,7 +5489,7 @@ impl ApiTester { let (payload_type, metadata) = self .client - .get_validator_blocks_v3::(slot, &randao_reveal, None, None) + .get_validator_blocks_v3::(slot, &randao_reveal, None, None, None) .await .unwrap(); Self::check_block_v3_metadata(&metadata, &payload_type); @@ -5557,7 +5557,7 @@ impl ApiTester { let (payload_type, metadata) = self .client - .get_validator_blocks_v3::(slot, &randao_reveal, None, None) + .get_validator_blocks_v3::(slot, &randao_reveal, None, None, None) .await .unwrap(); Self::check_block_v3_metadata(&metadata, &payload_type); @@ -5625,7 +5625,7 @@ impl ApiTester { let (payload_type, metadata) = self .client - .get_validator_blocks_v3::(slot, &randao_reveal, None, None) + .get_validator_blocks_v3::(slot, &randao_reveal, None, None, None) .await .unwrap(); Self::check_block_v3_metadata(&metadata, &payload_type); @@ -5691,7 +5691,7 @@ impl ApiTester { let (payload_type, metadata) = self .client - .get_validator_blocks_v3::(slot, &randao_reveal, None, None) + .get_validator_blocks_v3::(slot, &randao_reveal, None, None, None) .await .unwrap(); Self::check_block_v3_metadata(&metadata, &payload_type); @@ -5764,7 +5764,7 @@ impl ApiTester { let (payload_type, metadata) = self .client - .get_validator_blocks_v3::(slot, &randao_reveal, None, None) + .get_validator_blocks_v3::(slot, &randao_reveal, None, None, None) .await .unwrap(); Self::check_block_v3_metadata(&metadata, &payload_type); From 7cdfbf48e451f8c18c8aa9b6ed9b9449ca015e66 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Tue, 24 Jun 2025 11:28:37 +0800 Subject: [PATCH 25/42] cli check --- book/src/help_vc.md | 3 +++ validator_client/src/cli.rs | 2 +- validator_client/src/config.rs | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/book/src/help_vc.md b/book/src/help_vc.md index 15b5c209a70..a3226304c93 100644 --- a/book/src/help_vc.md +++ b/book/src/help_vc.md @@ -214,6 +214,9 @@ Flags: automatically enabled for <= 64 validators. Enabling this metric for higher validator counts will lead to higher volume of prometheus metrics being collected. + --graffiti-append + When used, client version info will be automatically appended to user + custom graffiti. -h, --help Prints help information --http diff --git a/validator_client/src/cli.rs b/validator_client/src/cli.rs index dce4542e674..c7ae7a73a70 100644 --- a/validator_client/src/cli.rs +++ b/validator_client/src/cli.rs @@ -157,7 +157,7 @@ pub struct ValidatorClient { display_order = 0, help_heading = FLAG_HEADER )] - pub graffiti_client: bool, + pub graffiti_append: bool, #[clap( long, diff --git a/validator_client/src/config.rs b/validator_client/src/config.rs index 14f9b6c975a..cd485a35b3f 100644 --- a/validator_client/src/config.rs +++ b/validator_client/src/config.rs @@ -234,7 +234,7 @@ impl Config { } } - config.graffiti_policy = if validator_client_config.graffiti_client { + config.graffiti_policy = if validator_client_config.graffiti_append { Some(GraffitiPolicy::AppendClientVersions) } else { Some(GraffitiPolicy::PreserveUserGraffiti) From 5f91558fabb24c160d1d28a722b99e66f83b2065 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Tue, 24 Jun 2025 21:19:44 +0800 Subject: [PATCH 26/42] append graffiti according to length --- beacon_node/execution_layer/src/engine_api.rs | 44 ++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/beacon_node/execution_layer/src/engine_api.rs b/beacon_node/execution_layer/src/engine_api.rs index 9eda81760cd..36c5d3c7789 100644 --- a/beacon_node/execution_layer/src/engine_api.rs +++ b/beacon_node/execution_layer/src/engine_api.rs @@ -734,7 +734,7 @@ impl ClientVersionV1 { lighthouse_commit_prefix: CommitPrefix, validator_graffiti: Option, ) -> Graffiti { - let base_graffiti = format!( + let append_graffiti_full = format!( "{}{}LH{}", self.code, self.commit @@ -749,12 +749,44 @@ impl ClientVersionV1 { .to_lowercase(), ); - let graffiti_string = match validator_graffiti { - Some(graffiti) => { - let graffiti_str = graffiti.as_utf8_lossy(); - format!("{} {}", base_graffiti, graffiti_str) + // Implement the special case here: + // https://hackmd.io/@wmoBhF17RAOH2NZ5bNXJVg/BJX2c9gja#SPECIAL-CASE-the-flexible-standard + let append_graffiti_one_byte = format!( + "{}{}LH{}", + self.code, + self.commit + .0 + .get(..2) + .map_or_else(|| self.commit.0.as_str(), |s| s) + .to_lowercase(), + lighthouse_commit_prefix + .0 + .get(..2) + .unwrap_or("00") + .to_lowercase(), + ); + + let append_graffiti_no_commit = format!("{}LH", self.code); + let append_graffiti_only_el = format!("{}", self.code); + + let graffiti_string = if let Some(graffiti) = validator_graffiti { + let graffiti_length = graffiti.as_utf8_lossy().len(); + let graffiti_str = graffiti.as_utf8_lossy(); + + if graffiti_length < 20 { + format!("{} {}", append_graffiti_full, graffiti_str) + } else if (20..24).contains(&graffiti_length) { + format!("{} {}", append_graffiti_one_byte, graffiti_str) + } else if (24..28).contains(&graffiti_length) { + format!("{} {}", append_graffiti_no_commit, graffiti_str) + } else if (28..30).contains(&graffiti_length) { + format!("{} {}", append_graffiti_only_el, graffiti_str) + } else { + return graffiti; } - None => base_graffiti, + } else { + // if no validator_graffiti (user doesn't specify), use the full client version info graffiti + append_graffiti_full }; let mut graffiti_bytes = [0u8; GRAFFITI_BYTES_LEN]; From e1b1ffcff893b8f926e66550bd8c54df99d0847e Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Tue, 24 Jun 2025 21:21:33 +0800 Subject: [PATCH 27/42] remove logging --- Cargo.lock | 1 - common/eth2/Cargo.toml | 1 - common/eth2/src/lib.rs | 3 --- validator_client/validator_services/src/block_service.rs | 3 +-- 4 files changed, 1 insertion(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0369ba91ad1..30be5fa233a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2742,7 +2742,6 @@ dependencies = [ "ssz_types", "test_random_derive", "tokio", - "tracing", "types", "zeroize", ] diff --git a/common/eth2/Cargo.toml b/common/eth2/Cargo.toml index 384f6f5049d..81666a64216 100644 --- a/common/eth2/Cargo.toml +++ b/common/eth2/Cargo.toml @@ -32,7 +32,6 @@ serde_json = { workspace = true } slashing_protection = { workspace = true } ssz_types = { workspace = true } test_random_derive = { path = "../../common/test_random_derive" } -tracing = { workspace = true } types = { workspace = true } zeroize = { workspace = true } diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index 80d21d29971..5939999d49f 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -37,7 +37,6 @@ use std::fmt; use std::future::Future; use std::path::PathBuf; use std::time::Duration; -use tracing::info; pub const V1: EndpointVersion = EndpointVersion(1); pub const V2: EndpointVersion = EndpointVersion(2); @@ -2213,8 +2212,6 @@ impl BeaconNodeHttpClient { path.query_pairs_mut() .append_pair("graffiti_policy", "AppendClientVersions"); } - // TODO, will be removed later - info!(url = %path, "Complete query url"); Ok(path) } diff --git a/validator_client/validator_services/src/block_service.rs b/validator_client/validator_services/src/block_service.rs index 960ae24854f..aadbf294db1 100644 --- a/validator_client/validator_services/src/block_service.rs +++ b/validator_client/validator_services/src/block_service.rs @@ -452,8 +452,7 @@ impl BlockService { }; info!(slot = slot.as_u64(), "Requesting unsigned block"); - // TODO, will be removed later - info!(?self.graffiti_policy, "Graffiti policy is"); + // Request block from first responsive beacon node. // // Try the proposer nodes last, since it's likely that they don't have a From 9da5479d5ab1c716f5b9069238fd05045dbb4c83 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Wed, 25 Jun 2025 23:11:18 +0800 Subject: [PATCH 28/42] add basic test --- .../beacon_chain/src/graffiti_calculator.rs | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/beacon_node/beacon_chain/src/graffiti_calculator.rs b/beacon_node/beacon_chain/src/graffiti_calculator.rs index 83b2ac61226..d699917711e 100644 --- a/beacon_node/beacon_chain/src/graffiti_calculator.rs +++ b/beacon_node/beacon_chain/src/graffiti_calculator.rs @@ -376,6 +376,60 @@ mod tests { assert_eq!(found_graffiti_bytes, expected_graffiti_bytes); } + #[tokio::test] + async fn check_graffiti_with_el_version_support_append_graffiti() { + let spec = Arc::new(test_spec::()); + let harness = get_harness(VALIDATOR_COUNT, spec, None); + + let graffiti_str = "testing"; + let mut graffiti_bytes = [0; GRAFFITI_BYTES_LEN]; + graffiti_bytes[..graffiti_str.len()].copy_from_slice(graffiti_str.as_bytes()); + + let policy = GraffitiPolicy::AppendClientVersions; + let found_graffiti_bytes = harness + .chain + .graffiti_calculator + .get_graffiti(GraffitiSettings::Specified { + graffiti: Graffiti::from(graffiti_bytes), + policy, + }) + .await + .0; + + let mock_commit = DEFAULT_CLIENT_VERSION.commit.clone(); + let client_version_info = format!( + "{}{}{}{}", + DEFAULT_CLIENT_VERSION.code, + mock_commit + .strip_prefix("0x") + .unwrap_or("&mock_commit") + .get(0..4) + .expect("should get first 2 bytes in hex"), + "LH", + lighthouse_version::COMMIT_PREFIX + .get(0..4) + .expect("should get first 2 bytes in hex") + ); + + // There is a space between the client version info and user graffiti + // as defined in calculate_graffiti fn in execution_api.rs + let expected_graffiti_string = format!("{} {}", client_version_info, graffiti_str); + + let expected_graffiti_prefix_bytes = expected_graffiti_string.as_bytes(); + let expected_graffiti_prefix_len = + std::cmp::min(expected_graffiti_prefix_bytes.len(), GRAFFITI_BYTES_LEN); + + let found_graffiti_string = + std::str::from_utf8(&found_graffiti_bytes[..expected_graffiti_prefix_len]) + .expect("bytes should convert nicely to ascii"); + + info!(expected_graffiti_string, found_graffiti_string, "results"); + println!("expected graffiti string: '{}'", expected_graffiti_string); + println!("found graffiti string: '{}'", found_graffiti_string); + + assert_eq!(expected_graffiti_string, found_graffiti_string); + } + #[tokio::test] async fn check_graffiti_with_validator_specified_value() { let spec = Arc::new(test_spec::()); From f108d6024e8c320e4d503792f2c15b0be27f9614 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Thu, 26 Jun 2025 12:36:39 +0800 Subject: [PATCH 29/42] Add graffiti tests --- .../beacon_chain/src/graffiti_calculator.rs | 211 ++++++++++++++++-- beacon_node/execution_layer/src/engine_api.rs | 2 + 2 files changed, 197 insertions(+), 16 deletions(-) diff --git a/beacon_node/beacon_chain/src/graffiti_calculator.rs b/beacon_node/beacon_chain/src/graffiti_calculator.rs index d699917711e..7bd0f9c62a2 100644 --- a/beacon_node/beacon_chain/src/graffiti_calculator.rs +++ b/beacon_node/beacon_chain/src/graffiti_calculator.rs @@ -377,14 +377,40 @@ mod tests { } #[tokio::test] - async fn check_graffiti_with_el_version_support_append_graffiti() { + async fn check_graffiti_with_validator_specified_value() { + let spec = Arc::new(test_spec::()); + let harness = get_harness(VALIDATOR_COUNT, spec, None); + + let graffiti_str = "nice graffiti bro"; + let mut graffiti_bytes = [0u8; GRAFFITI_BYTES_LEN]; + graffiti_bytes[..graffiti_str.len()].copy_from_slice(graffiti_str.as_bytes()); + + let found_graffiti = harness + .chain + .graffiti_calculator + .get_graffiti(GraffitiSettings::new( + Some(Graffiti::from(graffiti_bytes)), + Some(GraffitiPolicy::PreserveUserGraffiti), + )) + .await; + + assert_eq!( + found_graffiti.to_string(), + "0x6e6963652067726166666974692062726f000000000000000000000000000000" + ); + } + + #[tokio::test] + async fn check_append_el_version_graffiti_user_graffiti_less_than_20_characters() { let spec = Arc::new(test_spec::()); let harness = get_harness(VALIDATOR_COUNT, spec, None); - let graffiti_str = "testing"; + // user graffiti is 19 characters + let graffiti_str = "lido staking pool!!"; let mut graffiti_bytes = [0; GRAFFITI_BYTES_LEN]; graffiti_bytes[..graffiti_str.len()].copy_from_slice(graffiti_str.as_bytes()); + // To test appending client version info with user specified graffiti let policy = GraffitiPolicy::AppendClientVersions; let found_graffiti_bytes = harness .chain @@ -397,7 +423,7 @@ mod tests { .0; let mock_commit = DEFAULT_CLIENT_VERSION.commit.clone(); - let client_version_info = format!( + let append_graffiti_string = format!( "{}{}{}{}", DEFAULT_CLIENT_VERSION.code, mock_commit @@ -413,7 +439,7 @@ mod tests { // There is a space between the client version info and user graffiti // as defined in calculate_graffiti fn in execution_api.rs - let expected_graffiti_string = format!("{} {}", client_version_info, graffiti_str); + let expected_graffiti_string = format!("{} {}", append_graffiti_string, graffiti_str); let expected_graffiti_prefix_bytes = expected_graffiti_string.as_bytes(); let expected_graffiti_prefix_len = @@ -431,26 +457,179 @@ mod tests { } #[tokio::test] - async fn check_graffiti_with_validator_specified_value() { + async fn check_append_el_version_graffiti_user_graffiti_from_20_to_23_characters() { let spec = Arc::new(test_spec::()); let harness = get_harness(VALIDATOR_COUNT, spec, None); - let graffiti_str = "nice graffiti bro"; - let mut graffiti_bytes = [0u8; GRAFFITI_BYTES_LEN]; + // user graffiti is 22 characters + let graffiti_str = "now this is pod racing"; + let mut graffiti_bytes = [0; GRAFFITI_BYTES_LEN]; graffiti_bytes[..graffiti_str.len()].copy_from_slice(graffiti_str.as_bytes()); - let found_graffiti = harness + // To test appending client version info with user specified graffiti + let policy = GraffitiPolicy::AppendClientVersions; + let found_graffiti_bytes = harness .chain .graffiti_calculator - .get_graffiti(GraffitiSettings::new( - Some(Graffiti::from(graffiti_bytes)), - Some(GraffitiPolicy::PreserveUserGraffiti), - )) - .await; + .get_graffiti(GraffitiSettings::Specified { + graffiti: Graffiti::from(graffiti_bytes), + policy, + }) + .await + .0; - assert_eq!( - found_graffiti.to_string(), - "0x6e6963652067726166666974692062726f000000000000000000000000000000" + let mock_commit = DEFAULT_CLIENT_VERSION.commit.clone(); + // only append 1 byte (2 characters from the EL and CL commit) + let append_graffiti_string = format!( + "{}{}{}{}", + DEFAULT_CLIENT_VERSION.code, + mock_commit + .strip_prefix("0x") + .unwrap_or("&mock_commit") + .get(0..2) + .expect("should get first 2 bytes in hex"), + "LH", + lighthouse_version::COMMIT_PREFIX + .get(0..2) + .expect("should get first 2 bytes in hex") ); + + let expected_graffiti_string = format!("{} {}", append_graffiti_string, graffiti_str); + + let expected_graffiti_prefix_bytes = expected_graffiti_string.as_bytes(); + let expected_graffiti_prefix_len = + std::cmp::min(expected_graffiti_prefix_bytes.len(), GRAFFITI_BYTES_LEN); + + let found_graffiti_string = + std::str::from_utf8(&found_graffiti_bytes[..expected_graffiti_prefix_len]) + .expect("bytes should convert nicely to ascii"); + + info!(expected_graffiti_string, found_graffiti_string, "results"); + println!("expected graffiti string: '{}'", expected_graffiti_string); + println!("found graffiti string: '{}'", found_graffiti_string); + + assert_eq!(expected_graffiti_string, found_graffiti_string); + } + + #[tokio::test] + async fn check_append_el_version_graffiti_user_graffiti_from_24_to_27_characters() { + let spec = Arc::new(test_spec::()); + let harness = get_harness(VALIDATOR_COUNT, spec, None); + + // user graffiti is 23 characters + let graffiti_str = "This is where the fun begin"; + let mut graffiti_bytes = [0; GRAFFITI_BYTES_LEN]; + graffiti_bytes[..graffiti_str.len()].copy_from_slice(graffiti_str.as_bytes()); + + // To test appending client version info with user specified graffiti + let policy = GraffitiPolicy::AppendClientVersions; + let found_graffiti_bytes = harness + .chain + .graffiti_calculator + .get_graffiti(GraffitiSettings::Specified { + graffiti: Graffiti::from(graffiti_bytes), + policy, + }) + .await + .0; + + // only append EL and CL clients code + let append_graffiti_string = format!("{}{}", DEFAULT_CLIENT_VERSION.code, "LH",); + + let expected_graffiti_string = format!("{} {}", append_graffiti_string, graffiti_str); + + let expected_graffiti_prefix_bytes = expected_graffiti_string.as_bytes(); + let expected_graffiti_prefix_len = + std::cmp::min(expected_graffiti_prefix_bytes.len(), GRAFFITI_BYTES_LEN); + + let found_graffiti_string = + std::str::from_utf8(&found_graffiti_bytes[..expected_graffiti_prefix_len]) + .expect("bytes should convert nicely to ascii"); + + info!(expected_graffiti_string, found_graffiti_string, "results"); + println!("expected graffiti string: '{}'", expected_graffiti_string); + println!("found graffiti string: '{}'", found_graffiti_string); + + assert_eq!(expected_graffiti_string, found_graffiti_string); + } + + #[tokio::test] + async fn check_append_el_version_graffiti_user_graffiti_from_28_to_29_characters() { + let spec = Arc::new(test_spec::()); + let harness = get_harness(VALIDATOR_COUNT, spec, None); + + // user graffiti is 29 characters + let graffiti_str = "I don't like sand, its coarse"; + let mut graffiti_bytes = [0; GRAFFITI_BYTES_LEN]; + graffiti_bytes[..graffiti_str.len()].copy_from_slice(graffiti_str.as_bytes()); + + // To test appending client version info with user specified graffiti + let policy = GraffitiPolicy::AppendClientVersions; + let found_graffiti_bytes = harness + .chain + .graffiti_calculator + .get_graffiti(GraffitiSettings::Specified { + graffiti: Graffiti::from(graffiti_bytes), + policy, + }) + .await + .0; + + // only append EL client code + let append_graffiti_string = format!("{}", DEFAULT_CLIENT_VERSION.code); + + let expected_graffiti_string = format!("{} {}", append_graffiti_string, graffiti_str); + + let expected_graffiti_prefix_bytes = expected_graffiti_string.as_bytes(); + let expected_graffiti_prefix_len = + std::cmp::min(expected_graffiti_prefix_bytes.len(), GRAFFITI_BYTES_LEN); + + let found_graffiti_string = + std::str::from_utf8(&found_graffiti_bytes[..expected_graffiti_prefix_len]) + .expect("bytes should convert nicely to ascii"); + + info!(expected_graffiti_string, found_graffiti_string, "results"); + println!("expected graffiti string: '{}'", expected_graffiti_string); + println!("found graffiti string: '{}'", found_graffiti_string); + + assert_eq!(expected_graffiti_string, found_graffiti_string); + } + + #[tokio::test] + async fn check_append_el_version_graffiti_user_graffiti_from_30_to_32_characters() { + let spec = Arc::new(test_spec::()); + let harness = get_harness(VALIDATOR_COUNT, spec, None); + + // user graffiti is 30 characters + let expected_graffiti_string = "I do not like sand, its coarse"; + let mut graffiti_bytes = [0; GRAFFITI_BYTES_LEN]; + graffiti_bytes[..expected_graffiti_string.len()] + .copy_from_slice(expected_graffiti_string.as_bytes()); + + // To test appending client version info with user specified graffiti + let policy = GraffitiPolicy::AppendClientVersions; + let found_graffiti_bytes = harness + .chain + .graffiti_calculator + .get_graffiti(GraffitiSettings::Specified { + graffiti: Graffiti::from(graffiti_bytes), + policy, + }) + .await + .0; + + let expected_graffiti_prefix_bytes = expected_graffiti_string.as_bytes(); + let expected_graffiti_prefix_len = + std::cmp::min(expected_graffiti_prefix_bytes.len(), GRAFFITI_BYTES_LEN); + + let found_graffiti_string = + std::str::from_utf8(&found_graffiti_bytes[..expected_graffiti_prefix_len]) + .expect("bytes should convert nicely to ascii"); + + info!(expected_graffiti_string, found_graffiti_string, "results"); + println!("expected graffiti string: '{}'", expected_graffiti_string); + println!("found graffiti string: '{}'", found_graffiti_string); + + assert_eq!(expected_graffiti_string, found_graffiti_string); } } diff --git a/beacon_node/execution_layer/src/engine_api.rs b/beacon_node/execution_layer/src/engine_api.rs index c546d537ea1..caa3a52d2aa 100644 --- a/beacon_node/execution_layer/src/engine_api.rs +++ b/beacon_node/execution_layer/src/engine_api.rs @@ -773,6 +773,8 @@ impl ClientVersionV1 { let graffiti_length = graffiti.as_utf8_lossy().len(); let graffiti_str = graffiti.as_utf8_lossy(); + // 12 characters for append_graffiti_full, plus one character for spacing + // that leaves user specified graffiti to be 32-12-1 = 19 characters max, i.e., <20 if graffiti_length < 20 { format!("{} {}", append_graffiti_full, graffiti_str) } else if (20..24).contains(&graffiti_length) { From aabeb97e2559bdd82d28b9aec68f8d7513ff3b79 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Thu, 26 Jun 2025 12:58:13 +0800 Subject: [PATCH 30/42] lint --- beacon_node/beacon_chain/src/graffiti_calculator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beacon_node/beacon_chain/src/graffiti_calculator.rs b/beacon_node/beacon_chain/src/graffiti_calculator.rs index 7bd0f9c62a2..b62e36458c9 100644 --- a/beacon_node/beacon_chain/src/graffiti_calculator.rs +++ b/beacon_node/beacon_chain/src/graffiti_calculator.rs @@ -576,7 +576,7 @@ mod tests { .0; // only append EL client code - let append_graffiti_string = format!("{}", DEFAULT_CLIENT_VERSION.code); + let append_graffiti_string = DEFAULT_CLIENT_VERSION.code.to_string(); let expected_graffiti_string = format!("{} {}", append_graffiti_string, graffiti_str); From 74b288de5d5358a95407cc6e0f11bc165869b696 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Thu, 26 Jun 2025 22:47:13 +0800 Subject: [PATCH 31/42] Add test http query path --- beacon_node/http_api/tests/tests.rs | 57 +++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index 2c6f9c9e8d6..74787f2cd16 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -6665,6 +6665,55 @@ impl ApiTester { } self } + + async fn get_validator_blocks_v3_path_graffiti_policy(self) -> Self { + let slot = self.chain.slot().unwrap(); + let epoch = self.chain.epoch().unwrap(); + let (_, randao_reveal) = self.get_test_randao(slot, epoch).await; + let graffiti = Some(Graffiti::from([0; GRAFFITI_BYTES_LEN])); + let builder_boost_factor = None; + + let default_path = self + .client + .get_validator_blocks_v3_path( + slot, + &randao_reveal, + graffiti.as_ref(), + SkipRandaoVerification::Yes, + builder_boost_factor, + Some(GraffitiPolicy::PreserveUserGraffiti), + ) + .await + .unwrap(); + + let query_default_path = default_path.query().unwrap_or(""); + // When GraffitiPolicy is PreserveUserGraffiti, the HTTP API query path should be the same as the default + // i.e., without graffity_policy in the query path + assert!(!query_default_path.contains("graffiti_policy"), "URL should not contain graffiti_policy parameter when using PreserveUserGraffiti. URL is: {}", + query_default_path); + + let append_path = self + .client + .get_validator_blocks_v3_path( + slot, + &randao_reveal, + graffiti.as_ref(), + SkipRandaoVerification::No, + builder_boost_factor, + Some(GraffitiPolicy::AppendClientVersions), + ) + .await + .unwrap(); + + let query_append_path = append_path.query().unwrap_or(""); + // When GraffitiPolicy is AppendClientVersions, the HTTP API query path should contain "graffiti_policy" + assert!( + query_append_path.contains("graffiti_policy"), + "URL should contain graffiti_policy=AppendClientVersions parameter. URL is: {}", + query_append_path + ); + self + } } async fn poll_events, eth2::Error>> + Unpin, E: EthSpec>( @@ -7812,3 +7861,11 @@ async fn get_beacon_rewards_blocks_electra() { .test_beacon_block_rewards_electra() .await; } + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn get_validator_blocks_v3_http_api_path() { + ApiTester::new() + .await + .get_validator_blocks_v3_path_graffiti_policy() + .await; +} From 74705d45f04fbb37dc5bf8467d02a993e40a25a9 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Thu, 26 Jun 2025 23:11:41 +0800 Subject: [PATCH 32/42] Add none in test --- beacon_node/http_api/tests/tests.rs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index 74787f2cd16..4fa0867a030 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -6673,6 +6673,7 @@ impl ApiTester { let graffiti = Some(Graffiti::from([0; GRAFFITI_BYTES_LEN])); let builder_boost_factor = None; + // Default case where GraffitiPolicy is None let default_path = self .client .get_validator_blocks_v3_path( @@ -6681,16 +6682,37 @@ impl ApiTester { graffiti.as_ref(), SkipRandaoVerification::Yes, builder_boost_factor, - Some(GraffitiPolicy::PreserveUserGraffiti), + None, ) .await .unwrap(); let query_default_path = default_path.query().unwrap_or(""); + // When GraffitiPolicy is None, the HTTP API query path should be the same as the default + assert!( + !query_default_path.contains("graffiti_policy"), + "URL should not contain graffiti_policy parameter (same as PreserveUserGraffiti). URL is: {}", + query_default_path + ); + + let preserve_path = self + .client + .get_validator_blocks_v3_path( + slot, + &randao_reveal, + graffiti.as_ref(), + SkipRandaoVerification::Yes, + builder_boost_factor, + Some(GraffitiPolicy::PreserveUserGraffiti), + ) + .await + .unwrap(); + + let query_preserve_path = preserve_path.query().unwrap_or(""); // When GraffitiPolicy is PreserveUserGraffiti, the HTTP API query path should be the same as the default // i.e., without graffity_policy in the query path - assert!(!query_default_path.contains("graffiti_policy"), "URL should not contain graffiti_policy parameter when using PreserveUserGraffiti. URL is: {}", - query_default_path); + assert!(!query_preserve_path.contains("graffiti_policy"), "URL should not contain graffiti_policy parameter when using PreserveUserGraffiti. URL is: {}", + query_preserve_path); let append_path = self .client From 6676e58e10328bb280cba9eed6f6de80eb63fa1e Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Wed, 2 Jul 2025 20:00:09 +0800 Subject: [PATCH 33/42] add assert_eq --- beacon_node/http_api/tests/tests.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index 4fa0867a030..8b1eb2beb29 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -6714,6 +6714,9 @@ impl ApiTester { assert!(!query_preserve_path.contains("graffiti_policy"), "URL should not contain graffiti_policy parameter when using PreserveUserGraffiti. URL is: {}", query_preserve_path); + // Assert that the current HTTP API path is the same as PreserveUserGraffiti + assert_eq!(query_default_path, query_preserve_path); + let append_path = self .client .get_validator_blocks_v3_path( From d53beab7cd2489f6c3bfc2a7285506af7ec2986a Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Wed, 13 Aug 2025 11:45:13 +0800 Subject: [PATCH 34/42] merge unstable --- beacon_node/beacon_chain/src/graffiti_calculator.rs | 2 ++ beacon_node/beacon_chain/src/test_utils.rs | 2 +- beacon_node/http_api/tests/tests.rs | 7 +++++-- validator_client/validator_services/src/block_service.rs | 1 + 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/beacon_node/beacon_chain/src/graffiti_calculator.rs b/beacon_node/beacon_chain/src/graffiti_calculator.rs index b4130d21cc0..fae3c299ba2 100644 --- a/beacon_node/beacon_chain/src/graffiti_calculator.rs +++ b/beacon_node/beacon_chain/src/graffiti_calculator.rs @@ -252,7 +252,9 @@ async fn engine_version_cache_refresh_service( #[cfg(test)] mod tests { use crate::ChainConfig; + use crate::graffiti_calculator::GraffitiSettings; use crate::test_utils::{BeaconChainHarness, EphemeralHarnessType, test_spec}; + use eth2::types::GraffitiPolicy; use execution_layer::EngineCapabilities; use execution_layer::test_utils::{DEFAULT_CLIENT_VERSION, DEFAULT_ENGINE_CAPABILITIES}; use std::sync::Arc; diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 6ab223ddda0..cfc1096b464 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -995,7 +995,7 @@ where // If we produce two blocks for the same slot, they hash up to the same value and // BeaconChain errors out with `DuplicateFullyImported`. Vary the graffiti so that we produce // different blocks each time. - let graffiti = Graffiti::from(self.rng.lock().gen::<[u8; 32]>()); + let graffiti = Graffiti::from(self.rng.lock().random::<[u8; 32]>()); let graffiti_settings = GraffitiSettings::new(Some(graffiti), Some(GraffitiPolicy::PreserveUserGraffiti)); diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index 5434e583709..8330b34cf46 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -6742,8 +6742,11 @@ impl ApiTester { let query_preserve_path = preserve_path.query().unwrap_or(""); // When GraffitiPolicy is PreserveUserGraffiti, the HTTP API query path should be the same as the default // i.e., without graffity_policy in the query path - assert!(!query_preserve_path.contains("graffiti_policy"), "URL should not contain graffiti_policy parameter when using PreserveUserGraffiti. URL is: {}", - query_preserve_path); + assert!( + !query_preserve_path.contains("graffiti_policy"), + "URL should not contain graffiti_policy parameter when using PreserveUserGraffiti. URL is: {}", + query_preserve_path + ); // Assert that the current HTTP API path is the same as PreserveUserGraffiti assert_eq!(query_default_path, query_preserve_path); diff --git a/validator_client/validator_services/src/block_service.rs b/validator_client/validator_services/src/block_service.rs index 0be2e963885..cba11495b68 100644 --- a/validator_client/validator_services/src/block_service.rs +++ b/validator_client/validator_services/src/block_service.rs @@ -559,6 +559,7 @@ impl BlockService { randao_reveal_ref, graffiti.as_ref(), builder_boost_factor, + graffiti_policy, ) .await .map_err(|e| { From 1adc81a9bb05a5d513af21c89b3ea85bedd1931f Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Thu, 11 Dec 2025 18:56:54 +0800 Subject: [PATCH 35/42] fmt --- beacon_node/beacon_chain/src/test_utils.rs | 3 +-- validator_client/validator_services/src/block_service.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index a69d98405c6..1ebdbbc6be6 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -21,11 +21,10 @@ pub use crate::{ validator_monitor::{ValidatorMonitor, ValidatorMonitorConfig}, }; use bls::get_withdrawal_credentials; -use eth2::types::{GraffitiPolicy, SignedBlockContentsTuple}; use bls::{ AggregateSignature, Keypair, PublicKey, PublicKeyBytes, SecretKey, Signature, SignatureBytes, }; -use eth2::types::{GraffitiPolicy,SignedBlockContentsTuple}; +use eth2::types::{GraffitiPolicy, SignedBlockContentsTuple}; use execution_layer::test_utils::generate_genesis_header; use execution_layer::{ ExecutionLayer, diff --git a/validator_client/validator_services/src/block_service.rs b/validator_client/validator_services/src/block_service.rs index 219f03020cf..43d66c55729 100644 --- a/validator_client/validator_services/src/block_service.rs +++ b/validator_client/validator_services/src/block_service.rs @@ -1,5 +1,5 @@ use beacon_node_fallback::{ApiTopic, BeaconNodeFallback, Error as FallbackError, Errors}; -use bls::{PublicKeyBytes,SignatureBytes}; +use bls::{PublicKeyBytes, SignatureBytes}; use eth2::types::GraffitiPolicy; use eth2::{BeaconNodeHttpClient, StatusCode}; use graffiti_file::{GraffitiFile, determine_graffiti}; From d0c1b352fd32777ba3f61c87508b6bd7555253eb Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Thu, 11 Dec 2025 20:54:37 +0800 Subject: [PATCH 36/42] fix lint --- beacon_node/beacon_chain/src/test_utils.rs | 4 +++- validator_client/validator_services/src/block_service.rs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 1ebdbbc6be6..e4a385920f4 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -944,6 +944,8 @@ where // BeaconChain errors out with `DuplicateFullyImported`. Vary the graffiti so that we produce // different blocks each time. let graffiti = Graffiti::from(self.rng.lock().random::<[u8; 32]>()); + let graffiti_settings = + GraffitiSettings::new(Some(graffiti), Some(GraffitiPolicy::PreserveUserGraffiti)); let randao_reveal = self.sign_randao_reveal(&state, proposer_index, slot); @@ -957,7 +959,7 @@ where None, slot, randao_reveal, - Some(graffiti), + graffiti_settings, ProduceBlockVerification::VerifyRandao, builder_boost_factor, BlockProductionVersion::V3, diff --git a/validator_client/validator_services/src/block_service.rs b/validator_client/validator_services/src/block_service.rs index 43d66c55729..625f8db7cb9 100644 --- a/validator_client/validator_services/src/block_service.rs +++ b/validator_client/validator_services/src/block_service.rs @@ -1,5 +1,5 @@ use beacon_node_fallback::{ApiTopic, BeaconNodeFallback, Error as FallbackError, Errors}; -use bls::{PublicKeyBytes, SignatureBytes}; +use bls::PublicKeyBytes; use eth2::types::GraffitiPolicy; use eth2::{BeaconNodeHttpClient, StatusCode}; use graffiti_file::{GraffitiFile, determine_graffiti}; @@ -476,6 +476,7 @@ impl BlockService { randao_reveal_ref, graffiti.as_ref(), builder_boost_factor, + self_ref.graffiti_policy, ) .await }) @@ -502,6 +503,7 @@ impl BlockService { randao_reveal_ref, graffiti.as_ref(), builder_boost_factor, + self_ref.graffiti_policy, ) .await .map_err(|e| { From 5d94d0e0d4ab16145aa419ae9a24f472babf598e Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Mon, 15 Dec 2025 08:27:33 +0800 Subject: [PATCH 37/42] revise comments --- beacon_node/http_api/tests/tests.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index 19b36d5a0a1..fe565e84326 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -6887,7 +6887,7 @@ impl ApiTester { .unwrap(); let query_default_path = default_path.query().unwrap_or(""); - // When GraffitiPolicy is None, the HTTP API query path should be the same as the default + // When GraffitiPolicy is None, the HTTP API query path should not containt "graffiti_policy" assert!( !query_default_path.contains("graffiti_policy"), "URL should not contain graffiti_policy parameter (same as PreserveUserGraffiti). URL is: {}", @@ -6908,15 +6908,14 @@ impl ApiTester { .unwrap(); let query_preserve_path = preserve_path.query().unwrap_or(""); - // When GraffitiPolicy is PreserveUserGraffiti, the HTTP API query path should be the same as the default - // i.e., without graffity_policy in the query path + // When GraffitiPolicy is set to PreserveUserGraffiti, the HTTP API query path should not containt "graffiti_policy" assert!( !query_preserve_path.contains("graffiti_policy"), "URL should not contain graffiti_policy parameter when using PreserveUserGraffiti. URL is: {}", query_preserve_path ); - // Assert that the current HTTP API path is the same as PreserveUserGraffiti + // The HTTP API query path for PreserveUserGraffiti should be the same as the default assert_eq!(query_default_path, query_preserve_path); let append_path = self From c3c163cf5fb0788db5a75396909f1bacb4c981cd Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Mon, 15 Dec 2025 09:45:37 +0800 Subject: [PATCH 38/42] simplify test --- .../beacon_chain/src/graffiti_calculator.rs | 310 +++++------------- beacon_node/execution_layer/src/engine_api.rs | 4 + 2 files changed, 90 insertions(+), 224 deletions(-) diff --git a/beacon_node/beacon_chain/src/graffiti_calculator.rs b/beacon_node/beacon_chain/src/graffiti_calculator.rs index afdf1425128..5fe6563c31f 100644 --- a/beacon_node/beacon_chain/src/graffiti_calculator.rs +++ b/beacon_node/beacon_chain/src/graffiti_calculator.rs @@ -408,231 +408,93 @@ mod tests { let spec = Arc::new(test_spec::()); let harness = get_harness(VALIDATOR_COUNT, spec, None); + let graffiti_vec = vec![ + // less than 20 characters, example blow is 19 characters + "lido staking pool!!", + // 20-23 characters, example below is 22 characters + "now this is pod racing", + // 24-27 characters, example below is 27 characters + "This is where the fun begin", + // 28-29 characters, example below is 29 characters + "I don't like sand, its coarse", + // 30-32 characters, example below is 30 characters + "I do not like sand, its coarse", + ]; // user graffiti is 19 characters - let graffiti_str = "lido staking pool!!"; - let mut graffiti_bytes = [0; GRAFFITI_BYTES_LEN]; - graffiti_bytes[..graffiti_str.len()].copy_from_slice(graffiti_str.as_bytes()); - - // To test appending client version info with user specified graffiti - let policy = GraffitiPolicy::AppendClientVersions; - let found_graffiti_bytes = harness - .chain - .graffiti_calculator - .get_graffiti(GraffitiSettings::Specified { - graffiti: Graffiti::from(graffiti_bytes), - policy, - }) - .await - .0; - - let mock_commit = DEFAULT_CLIENT_VERSION.commit.clone(); - let append_graffiti_string = format!( - "{}{}{}{}", - DEFAULT_CLIENT_VERSION.code, - mock_commit - .strip_prefix("0x") - .unwrap_or("&mock_commit") - .get(0..4) - .expect("should get first 2 bytes in hex"), - "LH", - lighthouse_version::COMMIT_PREFIX - .get(0..4) - .expect("should get first 2 bytes in hex") - ); - - // There is a space between the client version info and user graffiti - // as defined in calculate_graffiti fn in execution_api.rs - let expected_graffiti_string = format!("{} {}", append_graffiti_string, graffiti_str); - - let expected_graffiti_prefix_bytes = expected_graffiti_string.as_bytes(); - let expected_graffiti_prefix_len = - std::cmp::min(expected_graffiti_prefix_bytes.len(), GRAFFITI_BYTES_LEN); - - let found_graffiti_string = - std::str::from_utf8(&found_graffiti_bytes[..expected_graffiti_prefix_len]) - .expect("bytes should convert nicely to ascii"); - - info!(expected_graffiti_string, found_graffiti_string, "results"); - println!("expected graffiti string: '{}'", expected_graffiti_string); - println!("found graffiti string: '{}'", found_graffiti_string); - - assert_eq!(expected_graffiti_string, found_graffiti_string); - } - - #[tokio::test] - async fn check_append_el_version_graffiti_user_graffiti_from_20_to_23_characters() { - let spec = Arc::new(test_spec::()); - let harness = get_harness(VALIDATOR_COUNT, spec, None); - // user graffiti is 22 characters - let graffiti_str = "now this is pod racing"; - let mut graffiti_bytes = [0; GRAFFITI_BYTES_LEN]; - graffiti_bytes[..graffiti_str.len()].copy_from_slice(graffiti_str.as_bytes()); - - // To test appending client version info with user specified graffiti - let policy = GraffitiPolicy::AppendClientVersions; - let found_graffiti_bytes = harness - .chain - .graffiti_calculator - .get_graffiti(GraffitiSettings::Specified { - graffiti: Graffiti::from(graffiti_bytes), - policy, - }) - .await - .0; - - let mock_commit = DEFAULT_CLIENT_VERSION.commit.clone(); - // only append 1 byte (2 characters from the EL and CL commit) - let append_graffiti_string = format!( - "{}{}{}{}", - DEFAULT_CLIENT_VERSION.code, - mock_commit - .strip_prefix("0x") - .unwrap_or("&mock_commit") - .get(0..2) - .expect("should get first 2 bytes in hex"), - "LH", - lighthouse_version::COMMIT_PREFIX - .get(0..2) - .expect("should get first 2 bytes in hex") - ); - - let expected_graffiti_string = format!("{} {}", append_graffiti_string, graffiti_str); - - let expected_graffiti_prefix_bytes = expected_graffiti_string.as_bytes(); - let expected_graffiti_prefix_len = - std::cmp::min(expected_graffiti_prefix_bytes.len(), GRAFFITI_BYTES_LEN); - - let found_graffiti_string = - std::str::from_utf8(&found_graffiti_bytes[..expected_graffiti_prefix_len]) - .expect("bytes should convert nicely to ascii"); - - info!(expected_graffiti_string, found_graffiti_string, "results"); - println!("expected graffiti string: '{}'", expected_graffiti_string); - println!("found graffiti string: '{}'", found_graffiti_string); - - assert_eq!(expected_graffiti_string, found_graffiti_string); - } - - #[tokio::test] - async fn check_append_el_version_graffiti_user_graffiti_from_24_to_27_characters() { - let spec = Arc::new(test_spec::()); - let harness = get_harness(VALIDATOR_COUNT, spec, None); - - // user graffiti is 23 characters - let graffiti_str = "This is where the fun begin"; - let mut graffiti_bytes = [0; GRAFFITI_BYTES_LEN]; - graffiti_bytes[..graffiti_str.len()].copy_from_slice(graffiti_str.as_bytes()); - - // To test appending client version info with user specified graffiti - let policy = GraffitiPolicy::AppendClientVersions; - let found_graffiti_bytes = harness - .chain - .graffiti_calculator - .get_graffiti(GraffitiSettings::Specified { - graffiti: Graffiti::from(graffiti_bytes), - policy, - }) - .await - .0; - - // only append EL and CL clients code - let append_graffiti_string = format!("{}{}", DEFAULT_CLIENT_VERSION.code, "LH",); - - let expected_graffiti_string = format!("{} {}", append_graffiti_string, graffiti_str); - - let expected_graffiti_prefix_bytes = expected_graffiti_string.as_bytes(); - let expected_graffiti_prefix_len = - std::cmp::min(expected_graffiti_prefix_bytes.len(), GRAFFITI_BYTES_LEN); - - let found_graffiti_string = - std::str::from_utf8(&found_graffiti_bytes[..expected_graffiti_prefix_len]) - .expect("bytes should convert nicely to ascii"); - - info!(expected_graffiti_string, found_graffiti_string, "results"); - println!("expected graffiti string: '{}'", expected_graffiti_string); - println!("found graffiti string: '{}'", found_graffiti_string); - - assert_eq!(expected_graffiti_string, found_graffiti_string); - } - - #[tokio::test] - async fn check_append_el_version_graffiti_user_graffiti_from_28_to_29_characters() { - let spec = Arc::new(test_spec::()); - let harness = get_harness(VALIDATOR_COUNT, spec, None); - - // user graffiti is 29 characters - let graffiti_str = "I don't like sand, its coarse"; - let mut graffiti_bytes = [0; GRAFFITI_BYTES_LEN]; - graffiti_bytes[..graffiti_str.len()].copy_from_slice(graffiti_str.as_bytes()); - - // To test appending client version info with user specified graffiti - let policy = GraffitiPolicy::AppendClientVersions; - let found_graffiti_bytes = harness - .chain - .graffiti_calculator - .get_graffiti(GraffitiSettings::Specified { - graffiti: Graffiti::from(graffiti_bytes), - policy, - }) - .await - .0; - - // only append EL client code - let append_graffiti_string = DEFAULT_CLIENT_VERSION.code.to_string(); - - let expected_graffiti_string = format!("{} {}", append_graffiti_string, graffiti_str); - - let expected_graffiti_prefix_bytes = expected_graffiti_string.as_bytes(); - let expected_graffiti_prefix_len = - std::cmp::min(expected_graffiti_prefix_bytes.len(), GRAFFITI_BYTES_LEN); - - let found_graffiti_string = - std::str::from_utf8(&found_graffiti_bytes[..expected_graffiti_prefix_len]) - .expect("bytes should convert nicely to ascii"); - - info!(expected_graffiti_string, found_graffiti_string, "results"); - println!("expected graffiti string: '{}'", expected_graffiti_string); - println!("found graffiti string: '{}'", found_graffiti_string); - - assert_eq!(expected_graffiti_string, found_graffiti_string); - } - - #[tokio::test] - async fn check_append_el_version_graffiti_user_graffiti_from_30_to_32_characters() { - let spec = Arc::new(test_spec::()); - let harness = get_harness(VALIDATOR_COUNT, spec, None); - - // user graffiti is 30 characters - let expected_graffiti_string = "I do not like sand, its coarse"; - let mut graffiti_bytes = [0; GRAFFITI_BYTES_LEN]; - graffiti_bytes[..expected_graffiti_string.len()] - .copy_from_slice(expected_graffiti_string.as_bytes()); - - // To test appending client version info with user specified graffiti - let policy = GraffitiPolicy::AppendClientVersions; - let found_graffiti_bytes = harness - .chain - .graffiti_calculator - .get_graffiti(GraffitiSettings::Specified { - graffiti: Graffiti::from(graffiti_bytes), - policy, - }) - .await - .0; - - let expected_graffiti_prefix_bytes = expected_graffiti_string.as_bytes(); - let expected_graffiti_prefix_len = - std::cmp::min(expected_graffiti_prefix_bytes.len(), GRAFFITI_BYTES_LEN); - - let found_graffiti_string = - std::str::from_utf8(&found_graffiti_bytes[..expected_graffiti_prefix_len]) - .expect("bytes should convert nicely to ascii"); - - info!(expected_graffiti_string, found_graffiti_string, "results"); - println!("expected graffiti string: '{}'", expected_graffiti_string); - println!("found graffiti string: '{}'", found_graffiti_string); - - assert_eq!(expected_graffiti_string, found_graffiti_string); + for graffiti in graffiti_vec { + let mut graffiti_bytes = [0; GRAFFITI_BYTES_LEN]; + graffiti_bytes[..graffiti.len()].copy_from_slice(graffiti.as_bytes()); + + // To test appending client version info with user specified graffiti + let policy = GraffitiPolicy::AppendClientVersions; + let found_graffiti_bytes = harness + .chain + .graffiti_calculator + .get_graffiti(GraffitiSettings::Specified { + graffiti: Graffiti::from(graffiti_bytes), + policy, + }) + .await + .0; + + let mock_commit = DEFAULT_CLIENT_VERSION.commit.clone(); + let graffiti_length = graffiti.len(); + + let append_graffiti_string = match graffiti_length { + 0..=19 => format!( + "{}{}{}{}", + DEFAULT_CLIENT_VERSION.code, + mock_commit + .strip_prefix("0x") + .unwrap_or("&mock_commit") + .get(0..4) + .expect("should get first 2 bytes in hex"), + "LH", + lighthouse_version::COMMIT_PREFIX + .get(0..4) + .expect("should get first 2 bytes in hex") + ), + 20..=23 => format!( + "{}{}{}{}", + DEFAULT_CLIENT_VERSION.code, + mock_commit + .strip_prefix("0x") + .unwrap_or("&mock_commit") + .get(0..2) + .expect("should get first 2 bytes in hex"), + "LH", + lighthouse_version::COMMIT_PREFIX + .get(0..2) + .expect("should get first 2 bytes in hex") + ), + 24..=27 => format!("{}{}", DEFAULT_CLIENT_VERSION.code, "LH",), + 28..=29 => DEFAULT_CLIENT_VERSION.code.to_string(), + // when user graffiti length is 30-32 characters, append nothing + 30..=32 => String::new(), + _ => panic!( + "graffiti length should be less than or equal to GRAFFITI_BYTES_LEN (32)" + ), + }; + + let expected_graffiti_string = if append_graffiti_string.is_empty() { + // for the case of empty append_graffiti_string + graffiti.to_string() + } else { + // There is a space between the client version info and user graffiti + // as defined in calculate_graffiti function in engine_api.rs + format!("{} {}", append_graffiti_string, graffiti) + }; + + let expected_graffiti_prefix_bytes = expected_graffiti_string.as_bytes(); + let expected_graffiti_prefix_len = + std::cmp::min(expected_graffiti_prefix_bytes.len(), GRAFFITI_BYTES_LEN); + + let found_graffiti_string = + std::str::from_utf8(&found_graffiti_bytes[..expected_graffiti_prefix_len]) + .expect("bytes should convert nicely to ascii"); + + assert_eq!(expected_graffiti_string, found_graffiti_string); + } } } diff --git a/beacon_node/execution_layer/src/engine_api.rs b/beacon_node/execution_layer/src/engine_api.rs index 9d15dae15b3..71bb7ad578a 100644 --- a/beacon_node/execution_layer/src/engine_api.rs +++ b/beacon_node/execution_layer/src/engine_api.rs @@ -811,12 +811,16 @@ impl ClientVersionV1 { // that leaves user specified graffiti to be 32-12-1 = 19 characters max, i.e., <20 if graffiti_length < 20 { format!("{} {}", append_graffiti_full, graffiti_str) + // user-specified graffiti is between 20-23 characters } else if (20..24).contains(&graffiti_length) { format!("{} {}", append_graffiti_one_byte, graffiti_str) + // user-specified graffiti is between 24-27 characters } else if (24..28).contains(&graffiti_length) { format!("{} {}", append_graffiti_no_commit, graffiti_str) + // user-specified graffiti is between 28-29 characters } else if (28..30).contains(&graffiti_length) { format!("{} {}", append_graffiti_only_el, graffiti_str) + // if user-specified graffiti is between 30-32 characters, append nothing } else { return graffiti; } From 6e1c56336292a6990eeee7cb4ba3699e3faf6ee2 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Mon, 15 Dec 2025 10:08:29 +0800 Subject: [PATCH 39/42] revise --- .../beacon_chain/src/graffiti_calculator.rs | 22 +++++++++---------- beacon_node/execution_layer/src/engine_api.rs | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/beacon_node/beacon_chain/src/graffiti_calculator.rs b/beacon_node/beacon_chain/src/graffiti_calculator.rs index 5fe6563c31f..f5635a88bd8 100644 --- a/beacon_node/beacon_chain/src/graffiti_calculator.rs +++ b/beacon_node/beacon_chain/src/graffiti_calculator.rs @@ -404,21 +404,21 @@ mod tests { } #[tokio::test] - async fn check_append_el_version_graffiti_user_graffiti_less_than_20_characters() { + async fn check_append_el_version_graffiti_various_length() { let spec = Arc::new(test_spec::()); let harness = get_harness(VALIDATOR_COUNT, spec, None); let graffiti_vec = vec![ // less than 20 characters, example blow is 19 characters - "lido staking pool!!", + "This is my graffiti", // 20-23 characters, example below is 22 characters - "now this is pod racing", - // 24-27 characters, example below is 27 characters - "This is where the fun begin", + "This is my graffiti yo", + // 24-27 characters, example below is 26 characters + "This is my graffiti string", // 28-29 characters, example below is 29 characters - "I don't like sand, its coarse", - // 30-32 characters, example below is 30 characters - "I do not like sand, its coarse", + "This is my graffiti string yo", + // 30-32 characters, example below is 32 characters + "This is my graffiti string yo yo", ]; // user graffiti is 19 characters @@ -439,8 +439,8 @@ mod tests { .0; let mock_commit = DEFAULT_CLIENT_VERSION.commit.clone(); - let graffiti_length = graffiti.len(); + let graffiti_length = graffiti.len(); let append_graffiti_string = match graffiti_length { 0..=19 => format!( "{}{}{}{}", @@ -473,12 +473,12 @@ mod tests { // when user graffiti length is 30-32 characters, append nothing 30..=32 => String::new(), _ => panic!( - "graffiti length should be less than or equal to GRAFFITI_BYTES_LEN (32)" + "graffiti length should be less than or equal to GRAFFITI_BYTES_LEN (32 characters)" ), }; let expected_graffiti_string = if append_graffiti_string.is_empty() { - // for the case of empty append_graffiti_string + // for the case of empty append_graffiti_string, i.e., user-specified graffiti is 30-32 characters graffiti.to_string() } else { // There is a space between the client version info and user graffiti diff --git a/beacon_node/execution_layer/src/engine_api.rs b/beacon_node/execution_layer/src/engine_api.rs index 15ae4d73d20..88567ac6e12 100644 --- a/beacon_node/execution_layer/src/engine_api.rs +++ b/beacon_node/execution_layer/src/engine_api.rs @@ -791,7 +791,7 @@ impl ClientVersionV1 { self.commit .0 .get(..2) - .map_or_else(|| self.commit.0.as_str(), |s| s) + .unwrap_or(self.commit.0.as_str()) .to_lowercase(), lighthouse_commit_prefix .0 From e40b49d0964090e503a0cbf7d98020a53c343de8 Mon Sep 17 00:00:00 2001 From: chonghe <44791194+chong-he@users.noreply.github.com> Date: Mon, 15 Dec 2025 16:54:19 +0800 Subject: [PATCH 40/42] Update beacon_node/beacon_chain/src/graffiti_calculator.rs Co-authored-by: Michael Sproul --- beacon_node/beacon_chain/src/graffiti_calculator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beacon_node/beacon_chain/src/graffiti_calculator.rs b/beacon_node/beacon_chain/src/graffiti_calculator.rs index f5635a88bd8..34d607b569a 100644 --- a/beacon_node/beacon_chain/src/graffiti_calculator.rs +++ b/beacon_node/beacon_chain/src/graffiti_calculator.rs @@ -409,7 +409,7 @@ mod tests { let harness = get_harness(VALIDATOR_COUNT, spec, None); let graffiti_vec = vec![ - // less than 20 characters, example blow is 19 characters + // less than 20 characters, example below is 19 characters "This is my graffiti", // 20-23 characters, example below is 22 characters "This is my graffiti yo", From 63746d8c3ad90eb21c5efbb2885ad078da3b3980 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Tue, 16 Dec 2025 08:12:45 +0800 Subject: [PATCH 41/42] revise --- beacon_node/http_api/tests/tests.rs | 4 ++-- book/src/help_vc.md | 5 +++-- validator_client/src/cli.rs | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index fe565e84326..ed7abead18a 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -6887,7 +6887,7 @@ impl ApiTester { .unwrap(); let query_default_path = default_path.query().unwrap_or(""); - // When GraffitiPolicy is None, the HTTP API query path should not containt "graffiti_policy" + // When GraffitiPolicy is None, the HTTP API query path should not contain "graffiti_policy" assert!( !query_default_path.contains("graffiti_policy"), "URL should not contain graffiti_policy parameter (same as PreserveUserGraffiti). URL is: {}", @@ -6908,7 +6908,7 @@ impl ApiTester { .unwrap(); let query_preserve_path = preserve_path.query().unwrap_or(""); - // When GraffitiPolicy is set to PreserveUserGraffiti, the HTTP API query path should not containt "graffiti_policy" + // When GraffitiPolicy is set to PreserveUserGraffiti, the HTTP API query path should not contain "graffiti_policy" assert!( !query_preserve_path.contains("graffiti_policy"), "URL should not contain graffiti_policy parameter when using PreserveUserGraffiti. URL is: {}", diff --git a/book/src/help_vc.md b/book/src/help_vc.md index c830e80c33e..2a9936d1d2f 100644 --- a/book/src/help_vc.md +++ b/book/src/help_vc.md @@ -222,8 +222,9 @@ Flags: higher validator counts will lead to higher volume of prometheus metrics being collected. --graffiti-append - When used, client version info will be automatically appended to user - custom graffiti. + When used, client version info will be prepended to user custom + graffiti, with a space in between. This should only be used with a + Lighthouse beacon node. -h, --help Prints help information --http diff --git a/validator_client/src/cli.rs b/validator_client/src/cli.rs index 00398ea64ca..3e1c46097f0 100644 --- a/validator_client/src/cli.rs +++ b/validator_client/src/cli.rs @@ -153,7 +153,8 @@ pub struct ValidatorClient { #[clap( long, requires = "graffiti", - help = "When used, client version info will be automatically appended to user custom graffiti.", + help = "When used, client version info will be prepended to user custom graffiti, with a space in between. \ + This should only be used with a Lighthouse beacon node.", display_order = 0, help_heading = FLAG_HEADER )] From 45a132870dd61adddca765ca97c9085f973a9a43 Mon Sep 17 00:00:00 2001 From: Tan Chee Keong Date: Tue, 16 Dec 2025 08:27:50 +0800 Subject: [PATCH 42/42] minor --- beacon_node/beacon_chain/src/graffiti_calculator.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/beacon_node/beacon_chain/src/graffiti_calculator.rs b/beacon_node/beacon_chain/src/graffiti_calculator.rs index 34d607b569a..85470715c9f 100644 --- a/beacon_node/beacon_chain/src/graffiti_calculator.rs +++ b/beacon_node/beacon_chain/src/graffiti_calculator.rs @@ -420,7 +420,6 @@ mod tests { // 30-32 characters, example below is 32 characters "This is my graffiti string yo yo", ]; - // user graffiti is 19 characters for graffiti in graffiti_vec { let mut graffiti_bytes = [0; GRAFFITI_BYTES_LEN];