Skip to content
This repository has been archived by the owner on Jun 3, 2020. It is now read-only.

Commit

Permalink
Upgrade to Abscissa 0.1.0-pre.1
Browse files Browse the repository at this point in the history
I'm preparing another release of Abscissa, which provides the
application boilerplate / framework used by Tendermint KMS:

https://github.com/iqlusioninc/abscissa

This commit contains what is hopefully the minimum set of changes
required to upgrade to the new version.

Notably it removes all TODOs about boilerplate code that could
potentially be replaced by custom derive, as `abscissa_derive` now
provides that functionality.

It also incorporates global application state, and the application as
the owner of the configuration.
  • Loading branch information
tony-iqlusion committed Jun 9, 2019
1 parent 4bf5224 commit 3b3bd7b
Show file tree
Hide file tree
Showing 57 changed files with 875 additions and 674 deletions.
619 changes: 382 additions & 237 deletions Cargo.lock

Large diffs are not rendered by default.

27 changes: 15 additions & 12 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,40 @@ members = [".", "tendermint-rs"]
[badges]
circle-ci = { repository = "tendermint/kms" }

[dependencies.abscissa]
version = "0.1.0-pre.1"
default-features = false
features = ["application", "signals", "secrets", "time"]

[dependencies]
abscissa = "0.0.6"
abscissa_derive = "0.0.2"
atomicwrites = "0.2"
byteorder = "1.2"
bytes = "0.4"
chrono = "0.4"
failure = "0.1"
failure_derive = "0.1"
gumdrop = "0.5"
hkdf = "0.7"
hmac = "0.7"
lazy_static = "1"
ledger-tendermint = { version = "0.4.0", optional = true }
log = "0.4"
prost-amino = "0.4.0"
prost-amino-derive = "0.4.0"
rand_os = "0.1"
serde = "1"
serde_derive = "1"
serde = { version = "1", features = ["serde_derive"] }
serde_json = "1"
sha2 = "0.8"
signal-hook = "0.1.7"
signatory = { version = "0.11.1", features = ["ed25519", "ecdsa"] }
signatory-dalek = "0.11"
signatory-secp256k1 = "0.11"
signatory = { version = "0.12", features = ["ed25519", "ecdsa"] }
signatory-dalek = "0.12"
signatory-secp256k1 = "0.12"
signatory-ledger-tm = { version = "0.12", optional = true }
subtle = "2"
subtle-encoding = { version = "0.3", features = ["bech32-preview"] }
tendermint = { version = "0.7", path = "tendermint-rs", features = ["amino-types", "secret-connection"] }
tiny-bip39 = "0.6"
wait-timeout = "0.2"
yubihsm = { version = "0.22", features = ["setup", "usb"], optional = true }
zeroize = "0.5"
yubihsm = { version = "0.25", features = ["setup", "usb"], optional = true }
zeroize = "0.9"

[dev-dependencies]
tempfile = "3"
Expand All @@ -55,7 +58,7 @@ rand = "0.6" # TODO: switch to the getrandom crate
[features]
default = []
softsign = []
ledgertm = ["ledger-tendermint"]
ledgertm = ["signatory-ledger-tm"]
yubihsm-mock = ["yubihsm/mockhsm"]

# Enable integer overflow checks in release builds for security reasons
Expand Down
96 changes: 87 additions & 9 deletions src/application.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,103 @@
//! Abscissa `Application` for the KMS

use abscissa::{self, Application, LoggingConfig};

use crate::{commands::KmsCommand, config::KmsConfig};
use abscissa::{application, Application, FrameworkError, LoggingConfig, StandardPaths};
use lazy_static::lazy_static;

lazy_static! {
/// Application state
pub static ref APPLICATION: application::Lock<KmsApplication> = application::Lock::default();
}

/// Obtain a read-only (multi-reader) lock on the application state.
///
/// Panics if the application state has not been initialized.
pub fn app_reader() -> application::lock::Reader<KmsApplication> {
APPLICATION.read()
}

/// Obtain an exclusive mutable lock on the application state.
pub fn app_writer() -> application::lock::Writer<KmsApplication> {
APPLICATION.write()
}

/// Obtain a read-only (multi-reader) lock on the application configuration.
///
/// Panics if the application configuration has not been loaded.
pub fn app_config() -> abscissa::config::Reader<KmsApplication> {
abscissa::config::Reader::new(&APPLICATION)
}

/// The `tmkms` application
#[derive(Debug)]
pub struct KmsApplication;
pub struct KmsApplication {
/// Application configuration.
config: Option<KmsConfig>,

/// Application state.
state: application::State<Self>,
}

impl KmsApplication {
/// Boot the application
// TODO: use the upstream implementation of this method
pub fn boot() {
abscissa::boot(KmsApplication)
impl Default for KmsApplication {
fn default() -> Self {
Self {
config: None,
state: application::State::default(),
}
}
}

impl Application for KmsApplication {
/// Entrypoint command for this application.
type Cmd = KmsCommand;
type Config = KmsConfig;

/// Application configuration.
type Cfg = KmsConfig;

/// Paths to resources within the application.
type Paths = StandardPaths;

/// Accessor for application configuration.
fn config(&self) -> Option<&KmsConfig> {
self.config.as_ref()
}

/// Borrow the application state immutably.
fn state(&self) -> &application::State<Self> {
&self.state
}

/// Borrow the application state mutably.
fn state_mut(&mut self) -> &mut application::State<Self> {
&mut self.state
}

/// Register all components used by this application.
///
/// If you would like to add additional components to your application
/// beyond the default ones provided by the framework, this is the place
/// to do so.
fn register_components(&mut self, command: &Self::Cmd) -> Result<(), FrameworkError> {
let components = self.framework_components(command)?;
self.state.components.register(components)
}

/// Post-configuration lifecycle callback.
///
/// Called regardless of whether config is loaded to indicate this is the
/// time in app lifecycle when configuration would be loaded if
/// possible.
fn after_config(&mut self, config: Option<Self::Cfg>) -> Result<(), FrameworkError> {
// Provide configuration to all component `after_config()` handlers
for component in self.state.components.iter_mut() {
component.after_config(config.as_ref())?;
}

self.config = config;
Ok(())
}

/// Get logging configuration from command-line options
fn logging_config(&self, command: &KmsCommand) -> LoggingConfig {
if command.verbose() {
LoggingConfig::verbose()
Expand Down
5 changes: 2 additions & 3 deletions src/bin/tmkms/main.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
//! Main entry point for the `tmkms` executable

extern crate tmkms;
use tmkms::KmsApplication;
use tmkms::application::APPLICATION;

/// Boot the `tmkms` application
fn main() {
KmsApplication::boot();
abscissa::boot(&APPLICATION);
}
6 changes: 3 additions & 3 deletions src/chain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub use self::{
};
use crate::{
config::{chain::ChainConfig, KmsConfig},
error::KmsError,
error::Error,
keyring::{self, KeyRing},
};
use std::{path::PathBuf, sync::Mutex};
Expand All @@ -31,7 +31,7 @@ pub struct Chain {

impl Chain {
/// Attempt to create a `Chain` state from the given configuration
pub fn from_config(config: &ChainConfig) -> Result<Chain, KmsError> {
pub fn from_config(config: &ChainConfig) -> Result<Chain, Error> {
let state_file = match config.state_file {
Some(ref path) => path.to_owned(),
None => PathBuf::from(&format!("{}_priv_validator_state.json", config.id)),
Expand Down Expand Up @@ -62,7 +62,7 @@ impl Chain {
}

/// Initialize the chain registry from the configuration file
pub fn load_config(config: &KmsConfig) -> Result<(), KmsError> {
pub fn load_config(config: &KmsConfig) -> Result<(), Error> {
for config in &config.chain {
REGISTRY.register(Chain::from_config(config)?)?;
}
Expand Down
10 changes: 6 additions & 4 deletions src/chain/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

use super::{Chain, Guard, Id};
use crate::{
error::{KmsError, KmsErrorKind::*},
error::{Error, ErrorKind::*},
keyring,
};
use lazy_static::lazy_static;
use std::{collections::BTreeMap, sync::RwLock};

lazy_static! {
/// State of Tendermint blockchain networks
pub static ref REGISTRY: GlobalRegistry = GlobalRegistry::default();
}

Expand All @@ -21,7 +23,7 @@ impl Registry {
&mut self,
chain_id: &Id,
signer: keyring::ed25519::Signer,
) -> Result<(), KmsError> {
) -> Result<(), Error> {
// TODO(tarcieri):
let chain = self.0.get_mut(chain_id).ok_or_else(|| {
err!(
Expand All @@ -36,7 +38,7 @@ impl Registry {
}

/// Register a `Chain` with the registry
pub fn register_chain(&mut self, chain: Chain) -> Result<(), KmsError> {
pub fn register_chain(&mut self, chain: Chain) -> Result<(), Error> {
let chain_id = chain.id;

if self.0.insert(chain_id, chain).is_none() {
Expand Down Expand Up @@ -72,7 +74,7 @@ impl GlobalRegistry {
}

/// Register a chain with the registry
pub fn register(&self, chain: Chain) -> Result<(), KmsError> {
pub fn register(&self, chain: Chain) -> Result<(), Error> {
// TODO(tarcieri): better handle `PoisonError` here?
let mut registry = self.0.write().unwrap();
registry.register_chain(chain)
Expand Down
5 changes: 3 additions & 2 deletions src/chain/state/hook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

use crate::{
config::chain::HookConfig,
error::{KmsError, KmsErrorKind::HookError},
error::{Error, ErrorKind::HookError},
};
use serde::Deserialize;
use std::{process::Command, time::Duration};
use tendermint::block;
use wait_timeout::ChildExt;
Expand All @@ -16,7 +17,7 @@ const DEFAULT_TIMEOUT_SECS: u64 = 1;
pub const BLOCK_HEIGHT_SANITY_LIMIT: u64 = 9000;

/// Run the given hook command to obtain the last signing state
pub fn run(config: &HookConfig) -> Result<Output, KmsError> {
pub fn run(config: &HookConfig) -> Result<Output, Error> {
let mut child = Command::new(&config.cmd[0])
.args(&config.cmd[1..])
.spawn()?;
Expand Down
4 changes: 2 additions & 2 deletions src/chain/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ mod error;
pub mod hook;

pub use self::error::{StateError, StateErrorKind};
use crate::error::{KmsError, KmsErrorKind::*};
use crate::error::{Error, ErrorKind::*};
use atomicwrites::{AtomicFile, OverwriteBehavior};
use serde_json;
use std::{
Expand All @@ -25,7 +25,7 @@ pub struct State {

impl State {
/// Load the state from the given path
pub fn load_state<P>(path: P) -> Result<State, KmsError>
pub fn load_state<P>(path: P) -> Result<State, Error>
where
P: AsRef<Path>,
{
Expand Down
17 changes: 9 additions & 8 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

use crate::{
config::ValidatorConfig,
error::{KmsError, KmsErrorKind},
error::{Error, ErrorKind},
keyring::SecretKeyEncoding,
session::Session,
};
Expand Down Expand Up @@ -120,7 +120,7 @@ fn tcp_session(
port: u16,
secret_key_path: &Path,
should_term: &Arc<AtomicBool>,
) -> Result<(), KmsError> {
) -> Result<(), Error> {
let secret_key = load_secret_connection_key(secret_key_path)?;

let node_public_key =
Expand All @@ -145,7 +145,7 @@ fn tcp_session(

session.request_loop(should_term)
})
.unwrap_or_else(|ref e| Err(KmsError::from_panic(e)))
.unwrap_or_else(|ref e| Err(Error::from_panic(e)))
}

/// Create a validator session over a Unix domain socket
Expand All @@ -154,7 +154,7 @@ fn unix_session(
max_height: Option<tendermint::block::Height>,
socket_path: &Path,
should_term: &Arc<AtomicBool>,
) -> Result<(), KmsError> {
) -> Result<(), Error> {
panic::catch_unwind(move || {
let mut session = Session::connect_unix(chain_id, max_height, socket_path)?;

Expand All @@ -166,16 +166,16 @@ fn unix_session(

session.request_loop(should_term)
})
.unwrap_or_else(|ref e| Err(KmsError::from_panic(e)))
.unwrap_or_else(|ref e| Err(Error::from_panic(e)))
}

/// Initialize KMS secret connection private key
fn load_secret_connection_key(path: &Path) -> Result<ed25519::Seed, KmsError> {
fn load_secret_connection_key(path: &Path) -> Result<ed25519::Seed, Error> {
if path.exists() {
Ok(
ed25519::Seed::decode_from_file(path, &SecretKeyEncoding::default()).map_err(|e| {
err!(
KmsErrorKind::ConfigError,
ErrorKind::ConfigError,
"error loading SecretConnection key from {}: {}",
path.display(),
e
Expand All @@ -184,7 +184,8 @@ fn load_secret_connection_key(path: &Path) -> Result<ed25519::Seed, KmsError> {
)
} else {
let seed = ed25519::Seed::generate();
seed.encode_to_file(path, &SecretKeyEncoding::default())?;
seed.encode_to_file(path, &SecretKeyEncoding::default())
.map_err(|_| Error::from(ErrorKind::IoError))?;
Ok(seed)
}
}
16 changes: 12 additions & 4 deletions src/commands/help.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
//! The `help` subcommand

use abscissa::{Callable, Command};
use abscissa::{Command, Runnable};

use super::KmsCommand;

/// The `help` subcommand
#[derive(Debug, Default, Options)]
pub struct HelpCommand {
/// Arguments to the `help` command
#[options(free)]
pub args: Vec<String>,
}

impl Callable for HelpCommand {
impl Runnable for HelpCommand {
/// Print help message
fn call(&self) {
KmsCommand::print_usage(self.args.as_slice());
fn run(&self) {
KmsCommand::print_usage(
&self
.args
.as_slice()
.iter()
.map(|arg| arg.as_ref())
.collect::<Vec<_>>(),
);
}
}
Loading

0 comments on commit 3b3bd7b

Please sign in to comment.