From 2f4a189d4783ede510e03399ec36aef57e2e03b0 Mon Sep 17 00:00:00 2001 From: Michael <68944931+michaelh-laine@users.noreply.github.com> Date: Tue, 6 Jun 2023 15:22:55 +0200 Subject: [PATCH] Hide Keybase and replace with URL (#31852) --- account-decoder/src/validator_info.rs | 2 +- cli/src/validator_info.rs | 54 +++++++++++++++++--- docs/src/running-validator/validator-info.md | 29 +++-------- 3 files changed, 54 insertions(+), 31 deletions(-) diff --git a/account-decoder/src/validator_info.rs b/account-decoder/src/validator_info.rs index 47e00a412a..7094fe2fb3 100644 --- a/account-decoder/src/validator_info.rs +++ b/account-decoder/src/validator_info.rs @@ -1,6 +1,6 @@ use solana_config_program::ConfigState; -pub const MAX_SHORT_FIELD_LENGTH: usize = 70; +pub const MAX_SHORT_FIELD_LENGTH: usize = 80; pub const MAX_LONG_FIELD_LENGTH: usize = 300; pub const MAX_VALIDATOR_INFO: u64 = 576; diff --git a/cli/src/validator_info.rs b/cli/src/validator_info.rs index 072a497082..3cd0f1a4b5 100644 --- a/cli/src/validator_info.rs +++ b/cli/src/validator_info.rs @@ -3,7 +3,7 @@ use { cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult}, spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount}, }, - bincode::deserialize, + bincode::{deserialize, serialized_size}, clap::{App, AppSettings, Arg, ArgMatches, SubCommand}, reqwest::blocking::Client, serde_json::{Map, Value}, @@ -41,6 +41,19 @@ pub fn check_details_length(string: String) -> Result<(), String> { } } +pub fn check_total_length(info: &ValidatorInfo) -> Result<(), String> { + let size = serialized_size(&info).unwrap(); + let limit = ValidatorInfo::max_space(); + + if size > limit { + Err(format!( + "Total size {size:?} exceeds limit of {limit:?} bytes" + )) + } else { + Ok(()) + } +} + // Return an error if url field is too long or cannot be parsed. pub fn check_url(string: String) -> Result<(), String> { is_url(string.clone())?; @@ -91,6 +104,10 @@ fn parse_args(matches: &ArgMatches<'_>) -> Value { if let Some(url) = matches.value_of("website") { map.insert("website".to_string(), Value::String(url.to_string())); } + + if let Some(icon_url) = matches.value_of("icon_url") { + map.insert("iconUrl".to_string(), Value::String(icon_url.to_string())); + } if let Some(details) = matches.value_of("details") { map.insert("details".to_string(), Value::String(details.to_string())); } @@ -161,6 +178,15 @@ impl ValidatorInfoSubCommands for App<'_, '_> { .validator(check_url) .help("Validator website url"), ) + .arg( + Arg::with_name("icon_url") + .short("i") + .long("icon-url") + .value_name("URL") + .takes_value(true) + .validator(check_url) + .help("Validator icon URL"), + ) .arg( Arg::with_name("keybase_username") .short("n") @@ -168,6 +194,7 @@ impl ValidatorInfoSubCommands for App<'_, '_> { .value_name("USERNAME") .takes_value(true) .validator(is_short_field) + .hidden(hidden_unless_forced()) // Being phased out .help("Validator Keybase username"), ) .arg( @@ -240,11 +267,11 @@ pub fn process_set_validator_info( ) -> ProcessResult { // Validate keybase username if let Some(string) = validator_info.get("keybaseUsername") { - let result = verify_keybase(&config.signers[0].pubkey(), string); - if result.is_err() { - if force_keybase { - println!("--force supplied, ignoring: {result:?}"); - } else { + if force_keybase { + println!("--force supplied, skipping Keybase verification"); + } else { + let result = verify_keybase(&config.signers[0].pubkey(), string); + if result.is_err() { result.map_err(|err| { CliError::BadParameter(format!("Invalid validator keybase username: {err}")) })?; @@ -255,6 +282,14 @@ pub fn process_set_validator_info( let validator_info = ValidatorInfo { info: validator_string, }; + + let result = check_total_length(&validator_info); + if result.is_err() { + result.map_err(|err| { + CliError::BadParameter(format!("Maximum size for validator info: {err}")) + })?; + } + // Check for existing validator-info account let all_config = rpc_client.get_program_accounts(&solana_config_program::id())?; let existing_account = all_config @@ -426,7 +461,7 @@ mod tests { fn test_check_url() { let url = "http://test.com"; assert_eq!(check_url(url.to_string()), Ok(())); - let long_url = "http://7cLvFwLCbyHuXQ1RGzhCMobAWYPMSZ3VbUml1qWi1nkc3FD7zj9hzTZzMvYJ.com"; + let long_url = "http://7cLvFwLCbyHuXQ1RGzhCMobAWYPMSZ3VbUml1CMobAWYPMSZ3VbUml1qWi1nkc3FD7zj9hzTZzMvYJ.com"; assert!(check_url(long_url.to_string()).is_err()); let non_url = "not parseable"; assert!(check_url(non_url.to_string()).is_err()); @@ -436,7 +471,7 @@ mod tests { fn test_is_short_field() { let name = "Alice Validator"; assert_eq!(is_short_field(name.to_string()), Ok(())); - let long_name = "Alice 7cLvFwLCbyHuXQ1RGzhCMobAWYPMSZ3VbUml1qWi1nkc3FD7zj9hzTZzMvYJt6rY9"; + let long_name = "Alice 7cLvFwLCbyHuXQ1RGzhCMobAWYPMSZ3VbUml1qWi1nkc3FD7zj9hzTZzMvYJt6rY9j9hzTZzMvYJt6rY9"; assert!(is_short_field(long_name.to_string()).is_err()); } @@ -460,6 +495,8 @@ mod tests { "Alice", "-n", "alice_keybase", + "-i", + "https://test.com/icon.png", ]); let subcommand_matches = matches.subcommand(); assert_eq!(subcommand_matches.0, "validator-info"); @@ -471,6 +508,7 @@ mod tests { let expected = json!({ "name": "Alice", "keybaseUsername": "alice_keybase", + "iconUrl": "https://test.com/icon.png", }); assert_eq!(parse_args(matches), expected); } diff --git a/docs/src/running-validator/validator-info.md b/docs/src/running-validator/validator-info.md index 9cb2a0caeb..9a29fe654a 100644 --- a/docs/src/running-validator/validator-info.md +++ b/docs/src/running-validator/validator-info.md @@ -18,12 +18,14 @@ For details about optional fields for VALIDATOR_INFO_ARGS: solana validator-info publish --help ``` +The recommended dimensions for the validator icon are 360x360px and PNG format. + ## Example Commands Example publish command: ```bash -solana validator-info publish "Elvis Validator" -n elvis -w "https://elvis-validates.com" +solana validator-info publish "Elvis Validator" -w "https://elvis-validates.com" -i "https://elvis-validates.com/my-icon.png" ``` Example query command: @@ -37,28 +39,11 @@ which outputs ```text Validator info from 8WdJvDz6obhADdxpGCiJKZsDYwTLNEDFizayqziDc9ah Validator pubkey: 6dMH3u76qZ7XG4bVboVRnBHR2FfrxEqTTTyj4xmyDMWo - Info: {"keybaseUsername":"elvis","name":"Elvis Validator","website":"https://elvis-validates.com"} + Info: {"iconUrl":"elvis","name":"Elvis Validator","website":"https://elvis-validates.com"} ``` +For older accounts instead of `iconUrl` you might see `keybaseUsername` as those accounts used Keybase for their validator icon (see next section for further information). + ## Keybase -Including a Keybase username allows client applications \(like the Solana -Network Explorer\) to automatically pull in your validator public profile, -including cryptographic proofs, brand identity, etc. To connect your validator -pubkey with Keybase: - -1. Join [https://keybase.io/](https://keybase.io/) and complete the profile for your validator -2. Add your validator **identity pubkey** to Keybase: - - - Create an empty file on your local computer called `validator-` - - In Keybase, navigate to the Files section, and upload your pubkey file to - - a `solana` subdirectory in your public folder: `/keybase/public//solana` - - - To check your pubkey, ensure you can successfully browse to - - `https://keybase.pub//solana/validator-` - -3. Add or update your `solana validator-info` with your Keybase username. The - - CLI will verify the `validator-` file +Previously Keybase was used by validators to provide their validator icon, however Keybase has sunset its service and thus is no longer supported. Some old validator info accounts will still contain keybase usernames this is reflected in the serialized data returend when querying these validator info accounts.