diff --git a/Cargo.lock b/Cargo.lock index e3666b40fd..4a20fca694 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2939,6 +2939,7 @@ dependencies = [ "bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "dirs 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.9.18 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/book/src/testnet-participation.md b/book/src/testnet-participation.md index ee786c3e01..2e2e247768 100644 --- a/book/src/testnet-participation.md +++ b/book/src/testnet-participation.md @@ -255,3 +255,20 @@ Available fields for VALIDATOR_INFO_ARGS: * Website * Keybase ID * Details + +##### Keybase + +Including a Keybase ID 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/ and complete the profile for your validator +2. Add your validator **identity pubkey** to Keybase: + * Create an empty file on your local computer called `solana_pubkey_` + * In Keybase, navigate to the Files section, and upload your pubkey file to + your public folder: `/keybase/public/` + * To check your pubkey, ensure you can successfully browse to + `https://keybase.pub//solana_pubkey_` +3. Add or update your `solana-validator-info` with your Keybase ID. The CLI will +verify the `solana_pubkey_` file diff --git a/validator-info/Cargo.toml b/validator-info/Cargo.toml index 0588315321..3393877abe 100644 --- a/validator-info/Cargo.toml +++ b/validator-info/Cargo.toml @@ -16,6 +16,7 @@ cuda = [] bincode = "1.1.4" clap = "2.33" dirs = "2.0.1" +reqwest = "0.9.18" serde = "1.0.94" serde_derive = "1.0.94" serde_json = "1.0.40" diff --git a/validator-info/src/validator_info.rs b/validator-info/src/validator_info.rs index 384569e922..20b8c6a69c 100644 --- a/validator-info/src/validator_info.rs +++ b/validator-info/src/validator_info.rs @@ -2,6 +2,7 @@ use bincode::deserialize; use clap::{ crate_description, crate_name, crate_version, App, AppSettings, Arg, ArgMatches, SubCommand, }; +use reqwest::Client; use serde_derive::{Deserialize, Serialize}; use serde_json::{Map, Value}; use solana_client::rpc_client::RpcClient; @@ -99,7 +100,30 @@ fn check_details_length(string: String) -> Result<(), String> { } } -fn parse_args(matches: &ArgMatches<'_>) -> Result> { +fn verify_keybase( + validator_pubkey: &Pubkey, + keybase_id: &Value, +) -> Result<(), Box> { + if let Some(keybase_id) = keybase_id.as_str() { + let url = format!( + "https://keybase.pub/{}/solana_pubkey_{:?}", + keybase_id, validator_pubkey + ); + let client = Client::new(); + if client.head(&url).send()?.status().is_success() { + Ok(()) + } else { + Err(format!("keybase_id could not be confirmed at: {}. Please add this pubkey file to your keybase profile to connect", url))? + } + } else { + Err(format!( + "keybase_id could not be parsed as String: {}", + keybase_id + ))? + } +} + +fn parse_args(matches: &ArgMatches<'_>) -> Value { let mut map = Map::new(); map.insert( "name".to_string(), @@ -117,8 +141,7 @@ fn parse_args(matches: &ArgMatches<'_>) -> Result> Value::String(keybase_id.to_string()), ); } - let string = serde_json::to_string(&Value::Object(map))?; - Ok(string) + Value::Object(map) } fn parse_validator_info(account_data: &[u8]) -> Result<(Pubkey, String), Box> { @@ -271,9 +294,13 @@ fn main() -> Result<(), Box> { // Prepare validator info let keys = vec![(id(), false), (validator_keypair.pubkey(), true)]; - let validator_info = parse_args(&matches)?; + let validator_info = parse_args(&matches); + if let Some(string) = validator_info.get("keybaseId") { + verify_keybase(&validator_keypair.pubkey(), &string)?; + } + let validator_string = serde_json::to_string(&validator_info)?; let validator_info = ValidatorInfo { - info: validator_info, + info: validator_string, }; // Check existence of validator-info account @@ -400,12 +427,11 @@ mod tests { .arg(Arg::with_name("keybase_id").short("k").takes_value(true)) .arg(Arg::with_name("details").short("d").takes_value(true)) .get_matches_from(vec!["test", "-n", "Alice", "-k", "464bb0f2956f7e83"]); - let expected_string = serde_json::to_string(&json!({ + let expected = json!({ "name": "Alice", "keybaseId": "464bb0f2956f7e83", - })) - .unwrap(); - assert_eq!(parse_args(&matches).unwrap(), expected_string); + }); + assert_eq!(parse_args(&matches), expected); } #[test]