diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index fca330ec480..466356b50da 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -3031,7 +3031,7 @@ pub mod args { pub const TEMPLATES_PATH: Arg = arg("templates-path"); pub const TIMEOUT_HEIGHT: ArgOpt = arg_opt("timeout-height"); pub const TIMEOUT_SEC_OFFSET: ArgOpt = arg_opt("timeout-sec-offset"); - pub const TM_ADDRESS: Arg = arg("tm-address"); + pub const TM_ADDRESS: ArgOpt = arg_opt("tm-address"); pub const TOKEN_OPT: ArgOpt = TOKEN.opt(); pub const TOKEN: Arg = arg("token"); pub const TOKEN_STR: Arg = arg("token"); @@ -5733,15 +5733,26 @@ pub mod args { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); let tm_addr = TM_ADDRESS.parse(matches); - Self { query, tm_addr } + let validator_addr = VALIDATOR_OPT.parse(matches); + Self { + query, + tm_addr, + validator_addr, + } } fn def(app: App) -> App { - app.add_args::>().arg( - TM_ADDRESS - .def() - .help("The address of the validator in Tendermint."), - ) + app.add_args::>() + .arg( + TM_ADDRESS + .def() + .help("The address of the validator in Tendermint."), + ) + .arg( + VALIDATOR_OPT + .def() + .help("The native address of the validator."), + ) } } @@ -5750,6 +5761,7 @@ pub mod args { QueryFindValidator:: { query: self.query.to_sdk(ctx), tm_addr: self.tm_addr, + validator_addr: self.validator_addr, } } } diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index bc57f58f149..716f8a72aed 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -2245,36 +2245,61 @@ pub async fn query_find_validator( context: &N, args: args::QueryFindValidator, ) { - let args::QueryFindValidator { query: _, tm_addr } = args; - if tm_addr.len() != 40 { - edisplay_line!( - context.io(), - "Expected 40 characters in Tendermint address, got {}", - tm_addr.len() - ); - cli::safe_exit(1); - } - let tm_addr = tm_addr.to_ascii_uppercase(); - let validator = unwrap_client_response::( - RPC.vp() - .pos() - .validator_by_tm_addr(context.client(), &tm_addr) - .await, - ); - match validator { - Some(address) => { - display_line!( + let args::QueryFindValidator { + query: _, + tm_addr, + mut validator_addr, + } = args; + if let Some(tm_addr) = tm_addr { + if tm_addr.len() != 40 { + edisplay_line!( context.io(), - "Found validator address \"{address}\"." - ) + "Expected 40 characters in Tendermint address, got {}", + tm_addr.len() + ); + cli::safe_exit(1); } - None => { - display_line!( - context.io(), - "No validator with Tendermint address {tm_addr} found." - ) + let tm_addr = tm_addr.to_ascii_uppercase(); + let validator = unwrap_client_response::( + RPC.vp() + .pos() + .validator_by_tm_addr(context.client(), &tm_addr) + .await, + ); + match validator { + Some(address) => { + display_line!( + context.io(), + "Found validator address \"{address}\"." + ); + if validator_addr.is_none() { + validator_addr = Some(address); + } + } + None => { + display_line!( + context.io(), + "No validator with Tendermint address {tm_addr} found." + ) + } } } + if let Some(validator_addr) = validator_addr { + let consensus_key = unwrap_client_response::( + RPC.vp() + .pos() + .consensus_key(context.client(), &validator_addr) + .await, + ); + let pkh: PublicKeyHash = (&consensus_key).into(); + display_line!(io, "Consensus key hash: {}", pkh); + display_line!(io, "Consensus key: {consensus_key}"); + display_line!( + io, + "Tendermint key: {}", + tm_consensus_key_raw_hash(&consensus_key) + ); + } } /// Get account's public key stored in its storage sub-space diff --git a/sdk/src/args.rs b/sdk/src/args.rs index c12ecdf5373..ec624b1fe27 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -1812,7 +1812,9 @@ pub struct QueryFindValidator { /// Common query args pub query: Query, /// Tendermint address - pub tm_addr: String, + pub tm_addr: Option, + /// Native validator address + pub validator_addr: Option
, } /// Query the raw bytes of given storage key diff --git a/sdk/src/queries/vp/pos.rs b/sdk/src/queries/vp/pos.rs index ff1b073dc41..355fc7ffa66 100644 --- a/sdk/src/queries/vp/pos.rs +++ b/sdk/src/queries/vp/pos.rs @@ -42,6 +42,8 @@ router! {POS, ( "validator" ) = { ( "is_validator" / [addr: Address] ) -> bool = is_validator, + ( "consensus_key" / [addr: Address] ) -> common::PublicKey = consensus_key, + ( "addresses" / [epoch: opt Epoch] ) -> HashSet
= validator_addresses, @@ -194,6 +196,24 @@ where namada_proof_of_stake::is_validator(ctx.wl_storage, &addr) } +/// Find a consensus key of a validator account. +fn consensus_key( + ctx: RequestCtx<'_, D, H, V, T>, + addr: Address, +) -> storage_api::Result +where + D: 'static + DB + for<'iter> DBIter<'iter> + Sync, + H: 'static + StorageHasher + Sync, +{ + let current_epoch = ctx.wl_storage.storage.last_epoch; + let params = read_pos_params(ctx.wl_storage)?; + namada_proof_of_stake::validator_consensus_key_handle(&addr).get( + ctx.wl_storage, + current_epoch, + ¶ms, + ) +} + /// Find if the given address is a delegator fn is_delegator( ctx: RequestCtx<'_, D, H, V, T>,