diff --git a/Cargo.lock b/Cargo.lock index 2095e5b..ba8fd44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1133,6 +1133,7 @@ dependencies = [ "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "signatory 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", "signatory-dalek 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "subtle-encoding 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tai64 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "x25519-dalek 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1200,6 +1201,7 @@ dependencies = [ "signatory-dalek 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "signatory-ledger-tm 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "signatory-secp256k1 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "subtle-encoding 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "tendermint 0.5.0-alpha1", diff --git a/Cargo.toml b/Cargo.toml index 01269db..277c87f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ signatory = { version = "0.11.1", features = ["ed25519", "ecdsa"] } signatory-dalek = "0.11" signatory-secp256k1 = "0.11" signatory-ledger-tm = { version = "0.11", optional = true } +subtle = "2" subtle-encoding = { version = "0.3", features = ["bech32-preview"] } tendermint = { version = "0.5.0-alpha1", path = "tendermint-rs" } tiny-bip39 = "0.6" diff --git a/src/client.rs b/src/client.rs index a5f7e95..13fba40 100644 --- a/src/client.rs +++ b/src/client.rs @@ -5,7 +5,13 @@ //! To dance around the fact the KMS isn't actually a service, we refer to it //! as a "Key Management System". -use signatory::{ed25519, Decode, Encode}; +use crate::{ + config::{ValidatorAddr, ValidatorConfig}, + error::{KmsError, KmsErrorKind}, + keyring::SecretKeyEncoding, + session::Session, +}; +use signatory::{ed25519, Decode, Encode, PublicKeyed}; use signatory_dalek::Ed25519Signer; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; @@ -15,14 +21,7 @@ use std::{ thread::{self, JoinHandle}, time::Duration, }; -use tendermint::{chain, public_keys::SecretConnectionKey}; - -use crate::{ - config::{ValidatorAddr, ValidatorConfig}, - error::{KmsError, KmsErrorKind}, - keyring::SecretKeyEncoding, - session::Session, -}; +use tendermint::{chain, secret_connection}; /// How long to wait after a crash before respawning (in seconds) pub const RESPAWN_DELAY: u64 = 1; @@ -69,8 +68,12 @@ fn client_loop(config: ValidatorConfig, should_term: &Arc) { } let session_result = match &addr { - ValidatorAddr::Tcp { host, port } => match &secret_key { - Some(path) => tcp_session(chain_id, host, *port, path, should_term), + ValidatorAddr::Tcp { + peer_id, + host, + port, + } => match &secret_key { + Some(path) => tcp_session(chain_id, *peer_id, host, *port, path, should_term), None => { error!( "config error: missing field `secret_key` for validator {}", @@ -104,6 +107,7 @@ fn client_loop(config: ValidatorConfig, should_term: &Arc) { /// Create a TCP connection to a validator (encrypted with SecretConnection) fn tcp_session( chain_id: chain::Id, + validator_peer_id: Option, host: &str, port: u16, secret_key_path: &Path, @@ -112,12 +116,13 @@ fn tcp_session( let secret_key = load_secret_connection_key(secret_key_path)?; let node_public_key = - SecretConnectionKey::from(ed25519::public_key(&Ed25519Signer::from(&secret_key)).unwrap()); + secret_connection::PublicKey::from(Ed25519Signer::from(&secret_key).public_key().unwrap()); info!("KMS node ID: {}", &node_public_key); panic::catch_unwind(move || { - let mut session = Session::connect_tcp(chain_id, host, port, &secret_key)?; + let mut session = + Session::connect_tcp(chain_id, validator_peer_id, host, port, &secret_key)?; info!( "[{}@tcp://{}:{}] connected to validator successfully", diff --git a/src/config/validator/addr.rs b/src/config/validator/addr.rs index c71e7eb..b3e5d56 100644 --- a/src/config/validator/addr.rs +++ b/src/config/validator/addr.rs @@ -1,18 +1,22 @@ //! Validator addresses (`tcp://` or `unix://`) +use crate::error::{KmsError, KmsErrorKind::*}; use serde::{de::Error as DeError, Deserialize, Deserializer}; use std::{ fmt::{self, Display}, path::PathBuf, str::{self, FromStr}, }; - -use crate::error::{KmsError, KmsErrorKind::*}; +use tendermint::secret_connection; #[derive(Clone, Debug)] pub enum ValidatorAddr { /// TCP connections (with SecretConnection transport encryption) Tcp { + /// Remote peer ID of the validator + // TODO(tarcieri): make this mandatory + peer_id: Option, + /// Validator hostname or IP address host: String, @@ -53,7 +57,14 @@ impl FromStr for ValidatorAddr { // TODO: less janky URL parser? (e.g. use `url` crate) fn from_str(addr: &str) -> Result { if addr.starts_with("tcp://") { - let host_and_port: Vec<&str> = addr[6..].split(':').collect(); + let authority_parts = addr[6..].split('@').collect::>(); + let (peer_id, authority) = match authority_parts.len() { + 1 => (None, authority_parts[0]), + 2 => (Some(authority_parts[0].parse()?), authority_parts[1]), + _ => fail!(ConfigError, "invalid tcp:// address: {}", addr), + }; + + let host_and_port: Vec<&str> = authority.split(':').collect(); if host_and_port.len() != 2 { fail!(ConfigError, "invalid tcp:// address: {}", addr); @@ -64,7 +75,11 @@ impl FromStr for ValidatorAddr { .parse() .map_err(|_| err!(ConfigError, "invalid tcp:// address (bad port): {}", addr))?; - Ok(ValidatorAddr::Tcp { host, port }) + Ok(ValidatorAddr::Tcp { + peer_id, + host, + port, + }) } else if addr.starts_with("unix://") { let socket_path = PathBuf::from(&addr[7..]); Ok(ValidatorAddr::Unix { socket_path }) diff --git a/src/session.rs b/src/session.rs index 1d4535a..31ea6ab 100644 --- a/src/session.rs +++ b/src/session.rs @@ -1,6 +1,14 @@ //! A session with a validator node -use signatory::ed25519; +use crate::{ + chain, + error::{KmsError, KmsErrorKind::VerificationError}, + keyring::KeyRing, + prost::Message, + rpc::{Request, Response, TendermintRequest}, + unix_connection::UnixConnection, +}; +use signatory::{ed25519, PublicKeyed}; use signatory_dalek::Ed25519Signer; use std::{ fmt::Debug, @@ -14,19 +22,10 @@ use std::{ Arc, }, }; +use subtle::ConstantTimeEq; use tendermint::{ amino_types::{PingRequest, PingResponse, PubKeyRequest}, - public_keys::SecretConnectionKey, - SecretConnection, -}; - -use crate::{ - chain, - error::KmsError, - keyring::KeyRing, - prost::Message, - rpc::{Request, Response, TendermintRequest}, - unix_connection::UnixConnection, + secret_connection::{self, SecretConnection}, }; /// Encrypted session with a validator node @@ -42,6 +41,7 @@ impl Session> { /// Create a new session with the validator at the given address/port pub fn connect_tcp( chain_id: chain::Id, + validator_peer_id: Option, host: &str, port: u16, secret_connection_key: &ed25519::Seed, @@ -50,8 +50,29 @@ impl Session> { let socket = TcpStream::connect(format!("{}:{}", host, port))?; let signer = Ed25519Signer::from(secret_connection_key); - let public_key = SecretConnectionKey::from(ed25519::public_key(&signer)?); + let public_key = secret_connection::PublicKey::from(signer.public_key()?); let connection = SecretConnection::new(socket, &public_key, &signer)?; + let actual_peer_id = connection.remote_pubkey().peer_id(); + + // TODO(tarcieri): move this logic into `SecretConnection::new`? + if let Some(expected_peer_id) = validator_peer_id { + if expected_peer_id.ct_eq(&actual_peer_id).unwrap_u8() == 0 { + fail!( + VerificationError, + "{}:{}: validator peer ID mismatch! (expected {}, got {})", + host, + port, + expected_peer_id, + actual_peer_id + ); + } + } else { + // TODO(tarcieri): make peer verification mandatory + warn!( + "[{}] {}:{}: unverified validator peer ID! ({})", + chain_id, host, port, actual_peer_id + ); + } Ok(Self { chain_id, diff --git a/tendermint-rs/Cargo.toml b/tendermint-rs/Cargo.toml index da92ef5..0f40a87 100644 --- a/tendermint-rs/Cargo.toml +++ b/tendermint-rs/Cargo.toml @@ -43,6 +43,7 @@ serde_derive = { version = "1", optional = true } signatory = { version = "0.11.2", optional = true, features = ["ed25519", "ecdsa"] } signatory-dalek = { version = "0.11", optional = true } sha2 = { version = "0.8", optional = true, default-features = false } +subtle = { version = "2", optional = true } subtle-encoding = { version = "0.3", features = ["bech32-preview"] } tai64 = { version = "1", optional = true, features = ["chrono"] } x25519-dalek = { version = "0.4.4", optional = true, default-features = false, features = ["u64_backend"] } @@ -60,6 +61,7 @@ secret-connection = [ "signatory", "signatory-dalek", "sha2", + "subtle", "x25519-dalek", "zeroize" ] diff --git a/tendermint-rs/src/public_keys.rs b/tendermint-rs/src/public_keys.rs index 7b5a0c0..7d331a6 100644 --- a/tendermint-rs/src/public_keys.rs +++ b/tendermint-rs/src/public_keys.rs @@ -1,13 +1,8 @@ //! Public keys used in Tendermint networks -// TODO:: account keys use crate::{amino_types::PubKeyResponse, error::Error}; -use sha2::{Digest, Sha256}; use signatory::{ecdsa::curve::secp256k1, ed25519}; -use std::{ - fmt::{self, Display}, - ops::Deref, -}; +use std::ops::Deref; use subtle_encoding::{bech32, hex}; /// Public keys allowed in Tendermint protocols @@ -112,70 +107,11 @@ impl Deref for TendermintKey { } } -/// Secret Connection signing keys -// TODO(tarcieri): unify with `TendermintKey`? -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -pub enum SecretConnectionKey { - /// Ed25519 Secret Connection keys - Ed25519(ed25519::PublicKey), -} - -impl SecretConnectionKey { - /// From raw Ed25519 public key bytes - pub fn from_raw_ed25519(bytes: &[u8]) -> Result { - Ok(SecretConnectionKey::Ed25519( - ed25519::PublicKey::from_bytes(bytes)?, - )) - } - - /// Get Ed25519 public key - pub fn ed25519(self) -> Option { - match self { - SecretConnectionKey::Ed25519(pk) => Some(pk), - } - } -} - -impl Display for SecretConnectionKey { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - SecretConnectionKey::Ed25519(ref pk) => { - for byte in &Sha256::digest(pk.as_bytes())[..20] { - write!(f, "{:02X}", byte)?; - } - } - } - Ok(()) - } -} - -impl From for SecretConnectionKey { - fn from(pk: ed25519::PublicKey) -> SecretConnectionKey { - SecretConnectionKey::Ed25519(pk) - } -} - #[cfg(test)] mod tests { - use super::{PublicKey, SecretConnectionKey, TendermintKey}; + use super::{PublicKey, TendermintKey}; use subtle_encoding::hex; - const EXAMPLE_SECRET_CONN_KEY: &str = - "F7FEB0B5BA0760B2C58893E329475D1EA81781DD636E37144B6D599AD38AA825"; - - #[test] - fn test_address_serialization() { - let example_key = SecretConnectionKey::from_raw_ed25519( - &hex::decode_upper(EXAMPLE_SECRET_CONN_KEY).unwrap(), - ) - .unwrap(); - - assert_eq!( - example_key.to_string(), - "117C95C4FD7E636C38D303493302D2C271A39669" - ); - } - const EXAMPLE_CONSENSUS_KEY: &str = "4A25C6640A1F72B9C975338294EF51B6D1C33158BB6ECBA69FBC3FB5A33C9DCE"; diff --git a/tendermint-rs/src/secret_connection/mod.rs b/tendermint-rs/src/secret_connection/mod.rs index 9be7cbb..8180aff 100644 --- a/tendermint-rs/src/secret_connection/mod.rs +++ b/tendermint-rs/src/secret_connection/mod.rs @@ -2,9 +2,11 @@ mod kdf; mod nonce; +mod peer_id; +mod public_key; -pub use self::{kdf::Kdf, nonce::Nonce}; -use crate::{amino_types::AuthSigMessage, error::Error, public_keys::SecretConnectionKey}; +pub use self::{kdf::Kdf, nonce::Nonce, peer_id::PeerId, public_key::PublicKey}; +use crate::{amino_types::AuthSigMessage, error::Error}; use byteorder::{ByteOrder, LE}; use bytes::BufMut; use prost::{encoding::encode_varint, Message}; @@ -34,20 +36,20 @@ pub struct SecretConnection { send_nonce: Nonce, recv_secret: aead::OpeningKey, send_secret: aead::SealingKey, - remote_pubkey: SecretConnectionKey, + remote_pubkey: PublicKey, recv_buffer: Vec, } impl SecretConnection { /// Returns authenticated remote pubkey - pub fn remote_pubkey(&self) -> SecretConnectionKey { + pub fn remote_pubkey(&self) -> PublicKey { self.remote_pubkey } #[allow(clippy::new_ret_no_self)] /// Performs handshake and returns a new authenticated SecretConnection. pub fn new( mut handler: IoHandler, - local_pubkey: &SecretConnectionKey, + local_pubkey: &PublicKey, local_privkey: &dyn Signer, ) -> Result, Error> { // Generate ephemeral keys for perfect forward secrecy. @@ -82,7 +84,7 @@ impl SecretConnection { .map_err(|_| Error::Crypto)?, send_secret: aead::SealingKey::new(&aead::CHACHA20_POLY1305, &kdf.send_secret) .map_err(|_| Error::Crypto)?, - remote_pubkey: SecretConnectionKey::from(ed25519::PublicKey::from_bytes( + remote_pubkey: PublicKey::from(ed25519::PublicKey::from_bytes( remote_eph_pubkey.as_bytes(), )?), }; @@ -92,7 +94,7 @@ impl SecretConnection { // Share (in secret) each other's pubkey & challenge signature let auth_sig_msg = match local_pubkey { - SecretConnectionKey::Ed25519(ref pk) => { + PublicKey::Ed25519(ref pk) => { share_auth_signature(&mut sc, pk.as_bytes(), local_signature)? } }; @@ -105,7 +107,7 @@ impl SecretConnection { ed25519::verify(&remote_verifier, &kdf.challenge, &remote_sig)?; // We've authorized. - sc.remote_pubkey = SecretConnectionKey::from(remote_pubkey); + sc.remote_pubkey = PublicKey::from(remote_pubkey); Ok(sc) } diff --git a/tendermint-rs/src/secret_connection/peer_id.rs b/tendermint-rs/src/secret_connection/peer_id.rs new file mode 100644 index 0000000..c6ee592 --- /dev/null +++ b/tendermint-rs/src/secret_connection/peer_id.rs @@ -0,0 +1,96 @@ +use crate::error::Error; +use serde::de::{self, Deserialize, Deserializer}; +use sha2::{Digest, Sha256}; +use signatory::ed25519; +use std::{ + fmt::{self, Display}, + str::FromStr, +}; +use subtle::{self, ConstantTimeEq}; +use subtle_encoding::hex; + +/// Size of a PeerId in bytes +pub const SIZE: usize = 20; + +/// SecretConnection Peer IDs +#[derive(Copy, Clone, Debug, Hash)] +pub struct PeerId([u8; SIZE]); + +impl PeerId { + /// Create a new PeerId from raw bytes + pub fn new(bytes: [u8; SIZE]) -> PeerId { + PeerId(bytes) + } + + /// Borrow the Peer ID as a byte slice + pub fn as_bytes(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl AsRef<[u8]> for PeerId { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +impl ConstantTimeEq for PeerId { + #[inline] + fn ct_eq(&self, other: &PeerId) -> subtle::Choice { + self.as_bytes().ct_eq(other.as_bytes()) + } +} + +impl Display for PeerId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for byte in &self.0 { + write!(f, "{:02X}", byte)?; + } + Ok(()) + } +} + +impl From for PeerId { + fn from(pk: ed25519::PublicKey) -> PeerId { + let digest = Sha256::digest(pk.as_bytes()); + let mut peer_id_bytes = [0u8; SIZE]; + peer_id_bytes.copy_from_slice(&digest[..SIZE]); + PeerId(peer_id_bytes) + } +} + +/// Decode PeerId from hex +impl FromStr for PeerId { + type Err = Error; + + fn from_str(s: &str) -> Result { + // Accept either upper or lower case hex + let bytes = hex::decode_upper(s) + .or_else(|_| hex::decode(s)) + .map_err(|_| Error::Parse)?; + + if bytes.len() != SIZE { + return Err(Error::Parse); + } + + let mut peer_id_bytes = [0u8; SIZE]; + peer_id_bytes.copy_from_slice(&bytes); + Ok(PeerId(peer_id_bytes)) + } +} + +impl<'de> Deserialize<'de> for PeerId { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + Self::from_str(&s).map_err(|_| { + de::Error::custom(format!( + "expected {}-character hex string, got {:?}", + SIZE * 2, + s + )) + }) + } +} diff --git a/tendermint-rs/src/secret_connection/public_key.rs b/tendermint-rs/src/secret_connection/public_key.rs new file mode 100644 index 0000000..451c0bf --- /dev/null +++ b/tendermint-rs/src/secret_connection/public_key.rs @@ -0,0 +1,67 @@ +//! Secret Connection peer public keys + +use super::peer_id::PeerId; +use crate::error::Error; +use signatory::ed25519; +use std::fmt::{self, Display}; + +/// Secret Connection peer public keys (signing, presently Ed25519-only) +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +pub enum PublicKey { + /// Ed25519 Secret Connection keys + Ed25519(ed25519::PublicKey), +} + +impl PublicKey { + /// From raw Ed25519 public key bytes + pub fn from_raw_ed25519(bytes: &[u8]) -> Result { + Ok(PublicKey::Ed25519(ed25519::PublicKey::from_bytes(bytes)?)) + } + + /// Get Ed25519 public key + pub fn ed25519(self) -> Option { + match self { + PublicKey::Ed25519(pk) => Some(pk), + } + } + + /// Get the remote Peer ID + pub fn peer_id(self) -> PeerId { + match self { + PublicKey::Ed25519(pk) => PeerId::from(pk), + } + } +} + +impl Display for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.peer_id()) + } +} + +impl From for PublicKey { + fn from(pk: ed25519::PublicKey) -> PublicKey { + PublicKey::Ed25519(pk) + } +} + +#[cfg(test)] +mod tests { + use super::PublicKey; + use subtle_encoding::hex; + + const EXAMPLE_SECRET_CONN_KEY: &str = + "F7FEB0B5BA0760B2C58893E329475D1EA81781DD636E37144B6D599AD38AA825"; + + #[test] + fn test_secret_connection_pubkey_serialization() { + let example_key = + PublicKey::from_raw_ed25519(&hex::decode_upper(EXAMPLE_SECRET_CONN_KEY).unwrap()) + .unwrap(); + + assert_eq!( + example_key.to_string(), + "117C95C4FD7E636C38D303493302D2C271A39669" + ); + } +} diff --git a/tests/integration.rs b/tests/integration.rs index 76dc363..7393fba 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -17,7 +17,7 @@ use std::{ use tempfile::NamedTempFile; use tendermint::{ amino_types::{self, *}, - SecretConnection, SecretConnectionKey, + secret_connection::{self, SecretConnection}, }; use tmkms::UnixConnection; @@ -126,6 +126,8 @@ impl KmsProcess { /// Create a config file for a TCP KMS and return its path fn create_tcp_config(port: u16) -> NamedTempFile { let mut config_file = NamedTempFile::new().unwrap(); + let peer_id = "f88883b673fc69d7869cab098de3bafc2ff76eb8"; + writeln!( config_file, r#" @@ -134,7 +136,7 @@ impl KmsProcess { key_format = {{ type = "bech32", account_key_prefix = "cosmospub", consensus_key_prefix = "cosmosvalconspub" }} [[validator]] - addr = "tcp://127.0.0.1:{}" + addr = "tcp://{}@127.0.0.1:{}" chain_id = "test_chain_id" reconnect = false secret_key = "tests/support/secret_connection.key" @@ -143,7 +145,7 @@ impl KmsProcess { chain_ids = ["test_chain_id"] path = "{}" "#, - port, SIGNING_KEY_PATH + peer_id, port, SIGNING_KEY_PATH ) .unwrap(); @@ -184,7 +186,8 @@ impl KmsProcess { // Here we reply to the kms with a "remote" ephermal key, auth signature etc: let socket_cp = sock.try_clone().unwrap(); - let public_key = SecretConnectionKey::from(signatory::public_key(&signer).unwrap()); + let public_key = + secret_connection::PublicKey::from(signatory::public_key(&signer).unwrap()); KmsConnection::SecretConnection( SecretConnection::new(socket_cp, &public_key, &signer).unwrap(), diff --git a/tests/support/gen-validator-integration-cfg.sh b/tests/support/gen-validator-integration-cfg.sh index 8d38ff4..6937706 100644 --- a/tests/support/gen-validator-integration-cfg.sh +++ b/tests/support/gen-validator-integration-cfg.sh @@ -7,7 +7,8 @@ GENESIS_FILE=${GENESIS_FILE:-${TMHOME}/config/genesis.json} SIGNING_KEY=${SIGNING_KEY:-${OUTPUT_PATH}/signing.key} SECRET_KEY=${SECRET_KEY:-${OUTPUT_PATH}/secret_connection.key} OUTPUT_FILE=${OUTPUT_FILE:-${OUTPUT_PATH}/tmkms.toml} -VALIDATOR_ADDR=${VALIDATOR_ADDR:-"tcp://127.0.0.1:61278"} +VALIDATOR_ID=${VALIDATOR_ID:-"f88883b673fc69d7869cab098de3bafc2ff76eb8"} +VALIDATOR_ADDR=${VALIDATOR_ADDR:-"tcp://f88883b673fc69d7869cab098de3bafc2ff76eb8@127.0.0.1:61278"} CFG_TEMPLATE=$(cat <<-EOF # Information about Tenderment blockchain networks this KMS services [[chain]] diff --git a/tests/support/kms_yubihsm_mock.toml b/tests/support/kms_yubihsm_mock.toml index 1cb3a31..af32a7d 100644 --- a/tests/support/kms_yubihsm_mock.toml +++ b/tests/support/kms_yubihsm_mock.toml @@ -8,7 +8,7 @@ id = "cosmoshub" key_format = { type = "bech32", account_key_prefix = "cosmospub", consensus_key_prefix = "cosmosvalconspub" } [[validator]] -addr = "tcp://127.0.0.1:23456" +addr = "tcp://f88883b673fc69d7869cab098de3bafc2ff76eb8@127.0.0.1:23456" chain_id = "test_chain_id" reconnect = false secret_key = "tests/seccon.key" diff --git a/tmkms.toml.example b/tmkms.toml.example index 8f4a32a..5fc4ea8 100644 --- a/tmkms.toml.example +++ b/tmkms.toml.example @@ -18,7 +18,8 @@ key_format = { type = "bech32", account_key_prefix = "cosmospub", consensus_key_ # Validator configuration [[validator]] -addr = "tcp://example1.example.com:26658" # or "unix:///path/to/socket" +addr = "tcp://f88883b673fc69d7869cab098de3bafc2ff76eb8@example1.example.com:26658" +# or addr = "unix:///path/to/socket" chain_id = "cosmoshub" reconnect = true # true is the default secret_key = "path/to/secret_connection.key"