11use crate :: BeaconChain ;
22use crate :: BeaconChainTypes ;
3+ use eth2:: types:: GraffitiPolicy ;
34use execution_layer:: { CommitPrefix , ExecutionLayer , http:: ENGINE_GET_CLIENT_VERSION_V1 } ;
45use logging:: crit;
56use 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+
5171pub 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) ]
225253mod 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}
0 commit comments