use signatory::{ed25519, Decode, Encode};
use signatory_dalek::Ed25519Signer;
use std::{
panic,
path::Path,
thread::{self, JoinHandle},
time::Duration,
};
use tendermint::{chain, public_keys::SecretConnectionKey};
use config::{ValidatorAddr, ValidatorConfig};
use error::{KmsError, KmsErrorKind};
use keyring::SecretKeyEncoding;
use session::Session;
pub const RESPAWN_DELAY: u64 = 1;
pub struct Client {
handle: JoinHandle<()>,
}
impl Client {
pub fn spawn(config: ValidatorConfig) -> Self {
Self {
handle: thread::spawn(move || client_loop(config)),
}
}
pub fn join(self) {
self.handle.join().unwrap();
}
}
fn client_loop(config: ValidatorConfig) {
let ValidatorConfig {
addr,
chain_id,
reconnect,
secret_key,
} = config;
loop {
let session_result = match &addr {
ValidatorAddr::Tcp { host, port } => match &secret_key {
Some(path) => tcp_session(chain_id, host, *port, path),
None => {
error!(
"config error: missing field `secret_key` for validator {}",
host
);
return;
}
},
ValidatorAddr::Unix { socket_path } => unix_session(chain_id, socket_path),
};
if let Err(e) = session_result {
error!("[{}@{}] {}", chain_id, addr, e);
if reconnect {
thread::sleep(Duration::from_secs(RESPAWN_DELAY));
} else {
return;
}
} else {
info!("[{}@{}] session closed gracefully", chain_id, &addr);
return;
}
}
}
fn tcp_session(
chain_id: chain::Id,
host: &str,
port: u16,
secret_key_path: &Path,
) -> Result<(), KmsError> {
let secret_key = load_secret_connection_key(secret_key_path)?;
let node_public_key =
SecretConnectionKey::from(ed25519::public_key(&Ed25519Signer::from(&secret_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)?;
info!(
"[{}@tcp://{}:{}] connected to validator successfully",
chain_id, host, port
);
session.request_loop()
}).unwrap_or_else(|ref e| Err(KmsError::from_panic(e)))
}
fn unix_session(chain_id: chain::Id, socket_path: &Path) -> Result<(), KmsError> {
panic::catch_unwind(move || {
let mut session = Session::accept_unix(chain_id, socket_path)?;
info!(
"[{}@unix://{}] waiting for a validator connection",
chain_id,
socket_path.display()
);
session.request_loop()
}).unwrap_or_else(|ref e| Err(KmsError::from_panic(e)))
}
fn load_secret_connection_key(path: &Path) -> Result<ed25519::Seed, KmsError> {
if path.exists() {
Ok(
ed25519::Seed::decode_from_file(path, &SecretKeyEncoding::default()).map_err(|e| {
err!(
KmsErrorKind::ConfigError,
"error loading SecretConnection key from {}: {}",
path.display(),
e
)
})?,
)
} else {
let seed = ed25519::Seed::generate();
seed.encode_to_file(path, &SecretKeyEncoding::default())?;
Ok(seed)
}
}