This repository has been archived by the owner on Jun 3, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #205 from tendermint/chain-registry
- Loading branch information
Showing
47 changed files
with
1,046 additions
and
510 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
/target | ||
**/*.rs.bk | ||
tmkms.toml | ||
**/*.rs.bk | ||
**/*priv_validator_state.json | ||
|
||
# Ignore VIM swap files | ||
*.swp | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
//! 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, | ||
}, | ||
|
||
/// Hex is a baseline representation | ||
#[serde(rename = "hex")] | ||
Hex, | ||
} | ||
|
||
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), | ||
}, | ||
Format::Hex => public_key.to_hex(), | ||
} | ||
} | ||
} | ||
|
||
/// 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)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
//! Information about particular Tendermint blockchain networks | ||
|
||
mod guard; | ||
pub mod key; | ||
pub mod registry; | ||
pub mod state; | ||
|
||
pub use self::{guard::Guard, registry::REGISTRY, state::State}; | ||
use crate::{config::chain::ChainConfig, error::KmsError}; | ||
use std::{path::PathBuf, sync::Mutex}; | ||
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, | ||
|
||
/// State from the last block signed for this chain | ||
pub state: Mutex<State>, | ||
} | ||
|
||
impl Chain { | ||
/// Attempt to create a `Chain` state from the given configuration | ||
pub fn from_config(config: &ChainConfig) -> Result<Chain, KmsError> { | ||
let state_file = match config.state_file { | ||
Some(ref path) => path.to_owned(), | ||
None => PathBuf::from(&format!("{}_priv_validator_state.json", config.id)), | ||
}; | ||
|
||
let mut state = State::load_state(state_file)?; | ||
|
||
if let Some(ref hook) = config.state_hook { | ||
match state::hook::run(hook) { | ||
Ok(hook_output) => state.update_from_hook_output(hook_output)?, | ||
Err(e) => { | ||
if hook.fail_closed { | ||
return Err(e); | ||
} else { | ||
// fail open: note the error to the log and proceed anyway | ||
error!("error invoking state hook for chain {}: {}", config.id, e); | ||
} | ||
} | ||
} | ||
} | ||
|
||
Ok(Self { | ||
id: config.id, | ||
key_format: config.key_format.clone(), | ||
state: Mutex::new(state), | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
//! Registry of information about known Tendermint blockchain networks | ||
|
||
use super::{Chain, Guard, Id}; | ||
use crate::{ | ||
config::chain::ChainConfig, | ||
error::{KmsError, KmsErrorKind::ConfigError}, | ||
}; | ||
use std::{collections::BTreeMap, sync::RwLock}; | ||
|
||
lazy_static! { | ||
pub static ref REGISTRY: Registry = Registry::default(); | ||
} | ||
|
||
/// Initialize the chain registry from the configuration file | ||
pub fn load_from_config(chain_configs: &[ChainConfig]) -> Result<(), KmsError> { | ||
for config in chain_configs { | ||
REGISTRY.register(Chain::from_config(config)?)?; | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
/// 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); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
use abscissa::Error; | ||
use std::fmt::{self, Display}; | ||
|
||
/// Error type | ||
#[derive(Debug)] | ||
pub struct StateError(pub(crate) Error<StateErrorKind>); | ||
|
||
/// Kinds of errors | ||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)] | ||
pub enum StateErrorKind { | ||
/// Height regressed | ||
#[fail(display = "height regression")] | ||
HeightRegression, | ||
|
||
/// Step regressed | ||
#[fail(display = "step regression")] | ||
StepRegression, | ||
|
||
/// Round regressed | ||
#[fail(display = "round regression")] | ||
RoundRegression, | ||
|
||
/// Double sign detected | ||
#[fail(display = "double sign detected")] | ||
DoubleSign, | ||
|
||
/// Error syncing state to disk | ||
#[fail(display = "error syncing state to disk")] | ||
SyncError, | ||
} | ||
|
||
impl From<Error<StateErrorKind>> for StateError { | ||
fn from(other: Error<StateErrorKind>) -> Self { | ||
StateError(other) | ||
} | ||
} | ||
|
||
impl Display for StateError { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
self.0.fmt(f) | ||
} | ||
} |
Oops, something went wrong.