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

Commit

Permalink
Initial chain registry (fixes #178)
Browse files Browse the repository at this point in the history
Adds a `chain::REGISTRY` (as a `lazy_static`) to tmkms, populated from
the configuration file.

Uses this registry when serializing keys to select the appropriate
serialization format.
  • Loading branch information
tony-iqlusion committed Mar 10, 2019
1 parent fb2a825 commit 3b36d42
Show file tree
Hide file tree
Showing 31 changed files with 503 additions and 229 deletions.
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions README.yubihsm.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ Below is an example of the command to generate and export an encrypted backup
of an Ed25519 signing key:

```
$ tmkms yubihsm keys generate 1 -l "steakz4u-validator:2019-03-06T01:25:39Z" -b steakz4u-validator-key.enc
$ tmkms yubihsm keys generate 1 -p cosmosvalconspub -b steakz4u-validator-key.enc
Generated key #1: cosmosvalconspub1zcjduepqtvzxa733n7dhrjf247n0jtdwsvvsd4jgqvzexj5tkwerpzy5sugsvmfja3
Wrote backup of key 1 (encrypted under wrap key 1) to steakz4u-validator-key.enc
```
Expand All @@ -327,6 +327,7 @@ the following sets of credentials:

- `tmkms yubihsm keys generate 1` - generates asymmetric key 0x0001, which is by
default an Ed25519 signing key.
- `-p` (or `--prefix`): Bech32 prefix to serialize key with (automatically sets label)
- `-l` (or `--label`): an up-to-40-character label describing the key
- `-b` (or `--backup`): path to a file where an *encrypted* backup of the
generated key should be written
Expand All @@ -342,7 +343,7 @@ The following command lists keys in the HSM:
```
$ tmkms yubihsm keys list
Listing keys in YubiHSM #9876543211:
- #1: cosmosvalconspub1zcjduepqtvzxa733n7dhrjf247n0jtdwsvvsd4jgqvzexj5tkwerpzy5sugsvmfja3
- 0x#0001: 1624DE64200FB6DB3175225219D290497E3B78190A3EEDA89AEBBC2E2294547CA98E76F9D5
```

## Exporting and Importing Keys
Expand Down
1 change: 1 addition & 0 deletions src/chain/chain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

19 changes: 19 additions & 0 deletions src/chain/guard.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use super::{Chain, Id};
use std::{collections::BTreeMap, sync::RwLockReadGuard};

/// Wrapper for a `RwLockReadGuard<'static, BTreeMap<Id, Chain>>`, allowing access to
/// global information about particular Tendermint networks / "chains"
pub struct Guard<'lock>(RwLockReadGuard<'lock, BTreeMap<Id, Chain>>);

impl<'lock> From<RwLockReadGuard<'lock, BTreeMap<Id, Chain>>> for Guard<'lock> {
fn from(guard: RwLockReadGuard<'lock, BTreeMap<Id, Chain>>) -> Guard<'lock> {
Guard(guard)
}
}

impl<'lock> Guard<'lock> {
/// Get information about a particular chain ID (if registered)
pub fn chain(&self, chain_id: Id) -> Option<&Chain> {
self.0.get(&chain_id)
}
}
42 changes: 42 additions & 0 deletions src/chain/key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//! Chain-specific key configuration

use super::{Id, REGISTRY};
use tendermint::TendermintKey;

/// Options for how keys for this chain are represented
#[derive(Clone, Debug, Deserialize)]
#[serde(tag = "type")]
pub enum Format {
/// Use the Bech32 serialization format with the given key prefixes
#[serde(rename = "bech32")]
Bech32 {
/// Prefix to use for Account keys
account_key_prefix: String,

/// Prefix to use for Consensus keys
consensus_key_prefix: String,
},
}

impl Format {
/// Serialize a `TendermintKey` according to chain-specific rules
pub fn serialize(&self, public_key: TendermintKey) -> String {
match self {
Format::Bech32 {
account_key_prefix,
consensus_key_prefix,
} => match public_key {
TendermintKey::AccountKey(pk) => pk.to_bech32(account_key_prefix),
TendermintKey::ConsensusKey(pk) => pk.to_bech32(consensus_key_prefix),
},
}
}
}

/// Serialize a key according to chain-specific serialization rules
pub fn serialize(chain_id: Id, public_key: TendermintKey) -> Option<String> {
let registry = REGISTRY.get();
registry
.chain(chain_id)
.map(|chain| chain.key_format.serialize(public_key))
}
27 changes: 27 additions & 0 deletions src/chain/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//! Information about particular Tendermint blockchain networks

mod guard;
pub mod key;
mod registry;

pub use self::{guard::Guard, registry::REGISTRY};
use crate::config::chain::ChainConfig;
pub use tendermint::chain::Id;

/// Information about a particular Tendermint blockchain network
pub struct Chain {
/// ID of a particular chain
pub id: Id,

/// Key format configuration
pub key_format: key::Format,
}

impl<'a> From<&ChainConfig> for Chain {
fn from(config: &ChainConfig) -> Chain {
Self {
id: config.id,
key_format: config.key_format.clone(),
}
}
}
42 changes: 42 additions & 0 deletions src/chain/registry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//! Registry of information about known Tendermint blockchain networks

use super::{Chain, Guard, Id};
use crate::error::{KmsError, KmsErrorKind::ConfigError};
use std::{collections::BTreeMap, sync::RwLock};

lazy_static! {
pub static ref REGISTRY: Registry = Registry::default();
}

/// Registry of blockchain networks known to the KMS
// The `RwLock` is a bit of futureproofing as this data structure is for the
// most part "immutable". New chains should be registered at boot time.
// The only case in which this structure may change is in the event of
// runtime configuration reloading, so the `RwLock` is included as
// futureproofing for such a feature.
// See: <https://github.com/tendermint/kms/issues/183>
#[derive(Default)]
pub struct Registry(RwLock<BTreeMap<Id, Chain>>);

impl Registry {
/// Acquire a read-only (concurrent) lock to the internal chain registry
pub fn get(&self) -> Guard {
// TODO(tarcieri): better handle `PoisonError` here?
self.0.read().unwrap().into()
}

/// Register a chain with the registry
pub fn register(&self, chain: Chain) -> Result<(), KmsError> {
// TODO(tarcieri): better handle `PoisonError` here?
let mut chain_map = self.0.write().unwrap();

let chain_id = chain.id;

if chain_map.insert(chain_id, chain).is_none() {
Ok(())
} else {
// TODO(tarcieri): handle updating the set of registered chains
fail!(ConfigError, "chain ID already registered: {}", chain_id);
}
}
}
20 changes: 14 additions & 6 deletions src/commands/start.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
use abscissa::{Callable, GlobalConfig};
use std::process;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::{thread, time};

use crate::{
chain,
client::Client,
config::{KmsConfig, ValidatorConfig},
keyring::KeyRing,
};
use abscissa::{Callable, GlobalConfig};
use std::{
process,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
thread, time,
};

/// The `start` command
#[derive(Debug, Options)]
Expand Down Expand Up @@ -42,6 +46,10 @@ impl Callable for StartCommand {

let config = KmsConfig::get_global();

for chain_config in &config.chain {
chain::REGISTRY.register(chain_config.into()).unwrap();
}

KeyRing::load_from_config(&config.providers).unwrap_or_else(|e| {
status_err!("couldn't load keyring: {}", e);
process::exit(1);
Expand Down
Loading

0 comments on commit 3b36d42

Please sign in to comment.