Skip to content

Commit 86c2b7c

Browse files
authored
Append client version info to graffiti (#7558)
* #7201 Co-Authored-By: Tan Chee Keong <tanck@sigmaprime.io> Co-Authored-By: chonghe <44791194+chong-he@users.noreply.github.com> Co-Authored-By: Jimmy Chen <jimmy@sigmaprime.io> Co-Authored-By: Tan Chee Keong <tanck2005@gmail.com>
1 parent afa6457 commit 86c2b7c

File tree

14 files changed

+392
-47
lines changed

14 files changed

+392
-47
lines changed

beacon_node/beacon_chain/src/beacon_chain.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use crate::events::ServerSentEventHandler;
3333
use crate::execution_payload::{NotifyExecutionLayer, PreparePayloadHandle, get_execution_payload};
3434
use crate::fetch_blobs::EngineGetBlobsOutput;
3535
use crate::fork_choice_signal::{ForkChoiceSignalRx, ForkChoiceSignalTx, ForkChoiceWaitResult};
36-
use crate::graffiti_calculator::GraffitiCalculator;
36+
use crate::graffiti_calculator::{GraffitiCalculator, GraffitiSettings};
3737
use crate::kzg_utils::reconstruct_blobs;
3838
use crate::light_client_finality_update_verification::{
3939
Error as LightClientFinalityUpdateError, VerifiedLightClientFinalityUpdate,
@@ -4493,7 +4493,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
44934493
self: &Arc<Self>,
44944494
randao_reveal: Signature,
44954495
slot: Slot,
4496-
validator_graffiti: Option<Graffiti>,
4496+
graffiti_settings: GraffitiSettings,
44974497
verification: ProduceBlockVerification,
44984498
builder_boost_factor: Option<u64>,
44994499
block_production_version: BlockProductionVersion,
@@ -4527,7 +4527,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
45274527
state_root_opt,
45284528
slot,
45294529
randao_reveal,
4530-
validator_graffiti,
4530+
graffiti_settings,
45314531
verification,
45324532
builder_boost_factor,
45334533
block_production_version,
@@ -5060,7 +5060,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
50605060
state_root_opt: Option<Hash256>,
50615061
produce_at_slot: Slot,
50625062
randao_reveal: Signature,
5063-
validator_graffiti: Option<Graffiti>,
5063+
graffiti_settings: GraffitiSettings,
50645064
verification: ProduceBlockVerification,
50655065
builder_boost_factor: Option<u64>,
50665066
block_production_version: BlockProductionVersion,
@@ -5071,7 +5071,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
50715071
let chain = self.clone();
50725072
let graffiti = self
50735073
.graffiti_calculator
5074-
.get_graffiti(validator_graffiti)
5074+
.get_graffiti(graffiti_settings)
50755075
.await;
50765076
let span = Span::current();
50775077
let mut partial_beacon_block = self

beacon_node/beacon_chain/src/graffiti_calculator.rs

Lines changed: 144 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::BeaconChain;
22
use crate::BeaconChainTypes;
3+
use eth2::types::GraffitiPolicy;
34
use execution_layer::{CommitPrefix, ExecutionLayer, http::ENGINE_GET_CLIENT_VERSION_V1};
45
use logging::crit;
56
use serde::{Deserialize, Serialize};
@@ -48,6 +49,25 @@ impl Debug for GraffitiOrigin {
4849
}
4950
}
5051

52+
pub enum GraffitiSettings {
53+
Unspecified,
54+
Specified {
55+
graffiti: Graffiti,
56+
policy: GraffitiPolicy,
57+
},
58+
}
59+
60+
impl GraffitiSettings {
61+
pub fn new(validator_graffiti: Option<Graffiti>, policy: Option<GraffitiPolicy>) -> Self {
62+
validator_graffiti
63+
.map(|graffiti| Self::Specified {
64+
graffiti,
65+
policy: policy.unwrap_or(GraffitiPolicy::PreserveUserGraffiti),
66+
})
67+
.unwrap_or(Self::Unspecified)
68+
}
69+
}
70+
5171
pub struct GraffitiCalculator<T: BeaconChainTypes> {
5272
pub beacon_graffiti: GraffitiOrigin,
5373
execution_layer: Option<ExecutionLayer<T::EthSpec>>,
@@ -73,11 +93,19 @@ impl<T: BeaconChainTypes> GraffitiCalculator<T> {
7393
/// 2. Graffiti specified by the user via beacon node CLI options.
7494
/// 3. The EL & CL client version string, applicable when the EL supports version specification.
7595
/// 4. The default lighthouse version string, used if the EL lacks version specification support.
76-
pub async fn get_graffiti(&self, validator_graffiti: Option<Graffiti>) -> Graffiti {
77-
if let Some(graffiti) = validator_graffiti {
78-
return graffiti;
96+
pub async fn get_graffiti(&self, graffiti_settings: GraffitiSettings) -> Graffiti {
97+
match graffiti_settings {
98+
GraffitiSettings::Specified { graffiti, policy } => match policy {
99+
GraffitiPolicy::PreserveUserGraffiti => graffiti,
100+
GraffitiPolicy::AppendClientVersions => {
101+
self.calculate_combined_graffiti(Some(graffiti)).await
102+
}
103+
},
104+
GraffitiSettings::Unspecified => self.calculate_combined_graffiti(None).await,
79105
}
106+
}
80107

108+
async fn calculate_combined_graffiti(&self, validator_graffiti: Option<Graffiti>) -> Graffiti {
81109
match self.beacon_graffiti {
82110
GraffitiOrigin::UserSpecified(graffiti) => graffiti,
83111
GraffitiOrigin::Calculated(default_graffiti) => {
@@ -133,7 +161,7 @@ impl<T: BeaconChainTypes> GraffitiCalculator<T> {
133161
CommitPrefix("00000000".to_string())
134162
});
135163

136-
engine_version.calculate_graffiti(lighthouse_commit_prefix)
164+
engine_version.calculate_graffiti(lighthouse_commit_prefix, validator_graffiti)
137165
}
138166
}
139167
}
@@ -224,8 +252,10 @@ async fn engine_version_cache_refresh_service<T: BeaconChainTypes>(
224252
#[cfg(test)]
225253
mod tests {
226254
use crate::ChainConfig;
255+
use crate::graffiti_calculator::GraffitiSettings;
227256
use crate::test_utils::{BeaconChainHarness, EphemeralHarnessType, test_spec};
228257
use bls::Keypair;
258+
use eth2::types::GraffitiPolicy;
229259
use execution_layer::EngineCapabilities;
230260
use execution_layer::test_utils::{DEFAULT_CLIENT_VERSION, DEFAULT_ENGINE_CAPABILITIES};
231261
use std::sync::Arc;
@@ -281,8 +311,12 @@ mod tests {
281311

282312
let version_bytes = std::cmp::min(lighthouse_version::VERSION.len(), GRAFFITI_BYTES_LEN);
283313
// grab the slice of the graffiti that corresponds to the lighthouse version
284-
let graffiti_slice =
285-
&harness.chain.graffiti_calculator.get_graffiti(None).await.0[..version_bytes];
314+
let graffiti_slice = &harness
315+
.chain
316+
.graffiti_calculator
317+
.get_graffiti(GraffitiSettings::Unspecified)
318+
.await
319+
.0[..version_bytes];
286320

287321
// convert graffiti bytes slice to ascii for easy debugging if this test should fail
288322
let graffiti_str =
@@ -303,7 +337,12 @@ mod tests {
303337
let spec = Arc::new(test_spec::<MinimalEthSpec>());
304338
let harness = get_harness(VALIDATOR_COUNT, spec, None);
305339

306-
let found_graffiti_bytes = harness.chain.graffiti_calculator.get_graffiti(None).await.0;
340+
let found_graffiti_bytes = harness
341+
.chain
342+
.graffiti_calculator
343+
.get_graffiti(GraffitiSettings::Unspecified)
344+
.await
345+
.0;
307346

308347
let mock_commit = DEFAULT_CLIENT_VERSION.commit.clone();
309348
let expected_graffiti_string = format!(
@@ -352,12 +391,109 @@ mod tests {
352391
let found_graffiti = harness
353392
.chain
354393
.graffiti_calculator
355-
.get_graffiti(Some(Graffiti::from(graffiti_bytes)))
394+
.get_graffiti(GraffitiSettings::new(
395+
Some(Graffiti::from(graffiti_bytes)),
396+
Some(GraffitiPolicy::PreserveUserGraffiti),
397+
))
356398
.await;
357399

358400
assert_eq!(
359401
found_graffiti.to_string(),
360402
"0x6e6963652067726166666974692062726f000000000000000000000000000000"
361403
);
362404
}
405+
406+
#[tokio::test]
407+
async fn check_append_el_version_graffiti_various_length() {
408+
let spec = Arc::new(test_spec::<MinimalEthSpec>());
409+
let harness = get_harness(VALIDATOR_COUNT, spec, None);
410+
411+
let graffiti_vec = vec![
412+
// less than 20 characters, example below is 19 characters
413+
"This is my graffiti",
414+
// 20-23 characters, example below is 22 characters
415+
"This is my graffiti yo",
416+
// 24-27 characters, example below is 26 characters
417+
"This is my graffiti string",
418+
// 28-29 characters, example below is 29 characters
419+
"This is my graffiti string yo",
420+
// 30-32 characters, example below is 32 characters
421+
"This is my graffiti string yo yo",
422+
];
423+
424+
for graffiti in graffiti_vec {
425+
let mut graffiti_bytes = [0; GRAFFITI_BYTES_LEN];
426+
graffiti_bytes[..graffiti.len()].copy_from_slice(graffiti.as_bytes());
427+
428+
// To test appending client version info with user specified graffiti
429+
let policy = GraffitiPolicy::AppendClientVersions;
430+
let found_graffiti_bytes = harness
431+
.chain
432+
.graffiti_calculator
433+
.get_graffiti(GraffitiSettings::Specified {
434+
graffiti: Graffiti::from(graffiti_bytes),
435+
policy,
436+
})
437+
.await
438+
.0;
439+
440+
let mock_commit = DEFAULT_CLIENT_VERSION.commit.clone();
441+
442+
let graffiti_length = graffiti.len();
443+
let append_graffiti_string = match graffiti_length {
444+
0..=19 => format!(
445+
"{}{}{}{}",
446+
DEFAULT_CLIENT_VERSION.code,
447+
mock_commit
448+
.strip_prefix("0x")
449+
.unwrap_or("&mock_commit")
450+
.get(0..4)
451+
.expect("should get first 2 bytes in hex"),
452+
"LH",
453+
lighthouse_version::COMMIT_PREFIX
454+
.get(0..4)
455+
.expect("should get first 2 bytes in hex")
456+
),
457+
20..=23 => format!(
458+
"{}{}{}{}",
459+
DEFAULT_CLIENT_VERSION.code,
460+
mock_commit
461+
.strip_prefix("0x")
462+
.unwrap_or("&mock_commit")
463+
.get(0..2)
464+
.expect("should get first 2 bytes in hex"),
465+
"LH",
466+
lighthouse_version::COMMIT_PREFIX
467+
.get(0..2)
468+
.expect("should get first 2 bytes in hex")
469+
),
470+
24..=27 => format!("{}{}", DEFAULT_CLIENT_VERSION.code, "LH",),
471+
28..=29 => DEFAULT_CLIENT_VERSION.code.to_string(),
472+
// when user graffiti length is 30-32 characters, append nothing
473+
30..=32 => String::new(),
474+
_ => panic!(
475+
"graffiti length should be less than or equal to GRAFFITI_BYTES_LEN (32 characters)"
476+
),
477+
};
478+
479+
let expected_graffiti_string = if append_graffiti_string.is_empty() {
480+
// for the case of empty append_graffiti_string, i.e., user-specified graffiti is 30-32 characters
481+
graffiti.to_string()
482+
} else {
483+
// There is a space between the client version info and user graffiti
484+
// as defined in calculate_graffiti function in engine_api.rs
485+
format!("{} {}", append_graffiti_string, graffiti)
486+
};
487+
488+
let expected_graffiti_prefix_bytes = expected_graffiti_string.as_bytes();
489+
let expected_graffiti_prefix_len =
490+
std::cmp::min(expected_graffiti_prefix_bytes.len(), GRAFFITI_BYTES_LEN);
491+
492+
let found_graffiti_string =
493+
std::str::from_utf8(&found_graffiti_bytes[..expected_graffiti_prefix_len])
494+
.expect("bytes should convert nicely to ascii");
495+
496+
assert_eq!(expected_graffiti_string, found_graffiti_string);
497+
}
498+
}
363499
}

beacon_node/beacon_chain/src/test_utils.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::blob_verification::GossipVerifiedBlob;
22
use crate::block_verification_types::{AsBlock, RpcBlock};
33
use crate::custody_context::NodeCustodyType;
44
use crate::data_column_verification::CustodyDataColumn;
5+
use crate::graffiti_calculator::GraffitiSettings;
56
use crate::kzg_utils::build_data_column_sidecars;
67
use crate::observed_operations::ObservationOutcome;
78
pub use crate::persisted_beacon_chain::PersistedBeaconChain;
@@ -23,7 +24,7 @@ use bls::get_withdrawal_credentials;
2324
use bls::{
2425
AggregateSignature, Keypair, PublicKey, PublicKeyBytes, SecretKey, Signature, SignatureBytes,
2526
};
26-
use eth2::types::SignedBlockContentsTuple;
27+
use eth2::types::{GraffitiPolicy, SignedBlockContentsTuple};
2728
use execution_layer::test_utils::generate_genesis_header;
2829
use execution_layer::{
2930
ExecutionLayer,
@@ -943,6 +944,8 @@ where
943944
// BeaconChain errors out with `DuplicateFullyImported`. Vary the graffiti so that we produce
944945
// different blocks each time.
945946
let graffiti = Graffiti::from(self.rng.lock().random::<[u8; 32]>());
947+
let graffiti_settings =
948+
GraffitiSettings::new(Some(graffiti), Some(GraffitiPolicy::PreserveUserGraffiti));
946949

947950
let randao_reveal = self.sign_randao_reveal(&state, proposer_index, slot);
948951

@@ -956,7 +959,7 @@ where
956959
None,
957960
slot,
958961
randao_reveal,
959-
Some(graffiti),
962+
graffiti_settings,
960963
ProduceBlockVerification::VerifyRandao,
961964
builder_boost_factor,
962965
BlockProductionVersion::V3,
@@ -1000,6 +1003,8 @@ where
10001003
// BeaconChain errors out with `DuplicateFullyImported`. Vary the graffiti so that we produce
10011004
// different blocks each time.
10021005
let graffiti = Graffiti::from(self.rng.lock().random::<[u8; 32]>());
1006+
let graffiti_settings =
1007+
GraffitiSettings::new(Some(graffiti), Some(GraffitiPolicy::PreserveUserGraffiti));
10031008

10041009
let randao_reveal = self.sign_randao_reveal(&state, proposer_index, slot);
10051010

@@ -1010,7 +1015,7 @@ where
10101015
None,
10111016
slot,
10121017
randao_reveal,
1013-
Some(graffiti),
1018+
graffiti_settings,
10141019
ProduceBlockVerification::VerifyRandao,
10151020
None,
10161021
BlockProductionVersion::FullV2,
@@ -1059,6 +1064,8 @@ where
10591064
// BeaconChain errors out with `DuplicateFullyImported`. Vary the graffiti so that we produce
10601065
// different blocks each time.
10611066
let graffiti = Graffiti::from(self.rng.lock().random::<[u8; 32]>());
1067+
let graffiti_settings =
1068+
GraffitiSettings::new(Some(graffiti), Some(GraffitiPolicy::PreserveUserGraffiti));
10621069

10631070
let randao_reveal = self.sign_randao_reveal(&state, proposer_index, slot);
10641071

@@ -1071,7 +1078,7 @@ where
10711078
None,
10721079
slot,
10731080
randao_reveal,
1074-
Some(graffiti),
1081+
graffiti_settings,
10751082
ProduceBlockVerification::VerifyRandao,
10761083
None,
10771084
BlockProductionVersion::FullV2,

0 commit comments

Comments
 (0)