Skip to content

Commit

Permalink
Add Era sub command in aggregator cli
Browse files Browse the repository at this point in the history
  • Loading branch information
jpraynaud committed Feb 17, 2023
1 parent c58daba commit ed6d680
Show file tree
Hide file tree
Showing 9 changed files with 269 additions and 12 deletions.
128 changes: 125 additions & 3 deletions mithril-aggregator/src/command_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,24 @@ use tokio::{sync::RwLock, time::Duration};
use mithril_common::{
certificate_chain::MithrilCertificateVerifier,
chain_observer::{CardanoCliRunner, ChainObserver},
crypto_helper::{key_decode_hex, ProtocolGenesisSigner, ProtocolGenesisVerifier},
crypto_helper::{
key_decode_hex, EraMarkersSigner, ProtocolGenesisSigner, ProtocolGenesisVerifier,
},
database::{ApplicationNodeType, DatabaseVersionChecker},
digesters::{
cache::{ImmutableFileDigestCacheProvider, JsonImmutableFileDigestCacheProviderBuilder},
CardanoImmutableDigester, ImmutableFileSystemObserver,
},
entities::{Epoch, HexEncodedGenesisSecretKey},
entities::{Epoch, HexEncodedEraMarkersSecretKey, HexEncodedGenesisSecretKey},
era::{EraChecker, EraReader},
store::{adapter::SQLiteAdapter, StakeStore},
BeaconProvider, BeaconProviderImpl,
};

use crate::{
configuration::EraConfiguration,
event_store::{self, TransmitterService},
tools::{GenesisTools, GenesisToolsDependency},
tools::{EraTools, EraToolsDependency, GenesisTools, GenesisToolsDependency},
AggregatorConfig, AggregatorRunner, AggregatorRuntime, CertificatePendingStore,
CertificateStore, Configuration, DefaultConfiguration, DependencyManager, GenesisConfiguration,
GzipSnapshotter, MithrilSignerRegisterer, MultiSignerImpl, ProtocolParametersStore,
Expand Down Expand Up @@ -101,6 +104,16 @@ fn setup_genesis_dependencies(
Ok(dependencies)
}

fn setup_era_dependencies(
_config: &EraConfiguration,
) -> Result<EraToolsDependency, Box<dyn std::error::Error>> {
let dependencies = EraToolsDependency {
era_markers_verifier: None,
};

Ok(dependencies)
}

async fn do_first_launch_initialization_if_needed(
chain_observer: Arc<dyn ChainObserver>,
protocol_parameters_store: Arc<ProtocolParametersStore>,
Expand Down Expand Up @@ -228,6 +241,7 @@ impl MainOpts {
#[derive(Debug, Clone, Subcommand)]
pub enum MainCommand {
Genesis(GenesisCommand),
Era(EraCommand),
Serve(ServeCommand),
}

Expand All @@ -238,6 +252,7 @@ impl MainCommand {
) -> Result<(), Box<dyn Error>> {
match self {
Self::Genesis(cmd) => cmd.execute(config_builder).await,
Self::Era(cmd) => cmd.execute(config_builder).await,
Self::Serve(cmd) => cmd.execute(config_builder).await,
}
}
Expand Down Expand Up @@ -650,3 +665,110 @@ impl BootstrapGenesisSubCommand {
.await
}
}

/// Era tools
#[derive(Parser, Debug, Clone)]
pub struct EraCommand {
/// commands
#[clap(subcommand)]
pub era_subcommand: EraSubCommand,
}

impl EraCommand {
pub async fn execute(
&self,
config_builder: ConfigBuilder<DefaultState>,
) -> Result<(), Box<dyn Error>> {
self.era_subcommand.execute(config_builder).await
}
}

/// Era tools commands.
#[derive(Debug, Clone, Subcommand)]
pub enum EraSubCommand {
/// Era list command.
List(ListEraSubCommand),

/// Era tx datum generate command.
GenerateTxDatum(GenerateTxDatumEraSubCommand),
}

impl EraSubCommand {
pub async fn execute(
&self,
config_builder: ConfigBuilder<DefaultState>,
) -> Result<(), Box<dyn Error>> {
match self {
Self::List(cmd) => cmd.execute(config_builder).await,
Self::GenerateTxDatum(cmd) => cmd.execute(config_builder).await,
}
}
}

/// Era list command
#[derive(Parser, Debug, Clone)]
pub struct ListEraSubCommand {}

impl ListEraSubCommand {
pub async fn execute(
&self,
config_builder: ConfigBuilder<DefaultState>,
) -> Result<(), Box<dyn Error>> {
let config: EraConfiguration = config_builder
.build()
.map_err(|e| format!("configuration build error: {e}"))?
.try_deserialize()
.map_err(|e| format!("configuration deserialize error: {e}"))?;
debug!("LIST ERA command"; "config" => format!("{config:?}"));
let dependencies = setup_era_dependencies(&config)?;
let era_tools = EraTools::from_dependencies(dependencies).await?;
print!("{}", era_tools.get_supported_eras_list()?);

Ok(())
}
}

/// Era tx datum generate command
#[derive(Parser, Debug, Clone)]
pub struct GenerateTxDatumEraSubCommand {
/// Current Era epoch
#[clap(long, env = "CURRENT_ERA_EPOCH")]
current_era_epoch: u64,

/// Next Era epoch start, if exists
#[clap(long, env = "NEXT_ERA_EPOCH")]
next_era_epoch: Option<u64>,

/// Era Markers Secret Key
#[clap(long, env = "ERA_MARKERS_SECRET_KEY")]
era_markers_secret_key: HexEncodedEraMarkersSecretKey,
}

impl GenerateTxDatumEraSubCommand {
pub async fn execute(
&self,
config_builder: ConfigBuilder<DefaultState>,
) -> Result<(), Box<dyn Error>> {
let config: EraConfiguration = config_builder
.build()
.map_err(|e| format!("configuration build error: {e}"))?
.try_deserialize()
.map_err(|e| format!("configuration deserialize error: {e}"))?;
debug!("GENERATE TXDATUM ERA command"; "config" => format!("{config:?}"));
let dependencies = setup_era_dependencies(&config)?;
let era_tools = EraTools::from_dependencies(dependencies).await?;

let era_markers_secret_key = key_decode_hex(&self.era_markers_secret_key)?;
let era_markers_signer = EraMarkersSigner::from_secret_key(era_markers_secret_key);
print!(
"{}",
era_tools.generate_tx_datum(
Epoch(self.current_era_epoch),
self.next_era_epoch.map(Epoch),
&era_markers_signer
)?
);

Ok(())
}
}
7 changes: 7 additions & 0 deletions mithril-aggregator/src/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,13 @@ impl GenesisConfiguration {
}
}

/// Configuration expected for Era commands.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EraConfiguration {
/// Era markers verification key
pub era_markers_verification_key: Option<String>,
}

/// Default configuration with all the default values for configurations.
#[derive(Debug, Clone)]
pub struct DefaultConfiguration {
Expand Down
110 changes: 110 additions & 0 deletions mithril-aggregator/src/tools/era.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use std::{error::Error, sync::Arc};

use mithril_common::{
chain_observer::{TxDatumBuilder, TxDatumFieldTypeName, TxDatumFieldValue},
crypto_helper::{key_encode_hex, EraMarkersSigner, EraMarkersVerifier},
entities::Epoch,
era::{adapters::EraMarkersPayloadCardanoChain, EraMarker, SupportedEra},
};

type EraToolsResult<R> = Result<R, Box<dyn Error>>;

pub struct EraToolsDependency {
/// Era markers signature verifier service.
pub era_markers_verifier: Option<Arc<EraMarkersVerifier>>,
}

pub struct EraTools {}

impl EraTools {
pub fn new() -> Self {
Self {}
}

pub async fn from_dependencies(_dependencies: EraToolsDependency) -> EraToolsResult<Self> {
Ok(Self::new())
}

/// Get list of supported eras
pub fn get_supported_eras_list(&self) -> EraToolsResult<String> {
Ok(serde_json::to_string(&SupportedEra::eras())?)
}

/// Generate TxDatum for eras
pub fn generate_tx_datum(
&self,
current_era_epoch: Epoch,
maybe_next_era_epoch: Option<Epoch>,
era_markers_signer: &EraMarkersSigner,
) -> EraToolsResult<String> {
if maybe_next_era_epoch.unwrap_or_default() >= current_era_epoch {
Err("next era epoch must be strictly greater than the current era epoch".to_string())?;
}

let mut era_markers = Vec::new();
for (index, era) in SupportedEra::eras().into_iter().enumerate() {
let era_marker = match index {
0 => EraMarker::new(&era.to_string(), Some(current_era_epoch)),
1 => EraMarker::new(&era.to_string(), maybe_next_era_epoch),
_ => unreachable!(),
};
era_markers.push(era_marker);
}
let era_markers_payload = EraMarkersPayloadCardanoChain {
markers: era_markers,
signature: None,
}
.sign(era_markers_signer)?;

let tx_datum = TxDatumBuilder::new()
.add_field(
TxDatumFieldTypeName::Bytes,
TxDatumFieldValue::Bytes(
key_encode_hex(era_markers_payload)
.map_err(|e| format!("era markerspayload could not be hex encoded: {e}"))?,
),
)
.build()?;

Ok(tx_datum.0)
}
}

#[cfg(test)]
mod tests {
use super::*;

fn build_tools() -> EraTools {
EraTools {}
}

#[test]
fn get_supported_eras_list() {
let era_tools = build_tools();
let supported_eras_list_serialized = era_tools
.get_supported_eras_list()
.expect("get_supported_eras_list should not fail");
let supported_eras_list: Vec<SupportedEra> =
serde_json::from_str(&supported_eras_list_serialized)
.expect("supported_eras_list_serialized should be a valid JSON");
assert_eq!(supported_eras_list, SupportedEra::eras());
}

#[test]
fn generate_tx_datum_ok() {
let era_markers_signer = EraMarkersSigner::create_deterministic_signer();
let era_tools = build_tools();
let _ = era_tools
.generate_tx_datum(Epoch(1), None, &era_markers_signer)
.expect("generate_tx_datum should not fail");
}

#[test]
fn generate_tx_datum_wrong_epochs() {
let era_markers_signer = EraMarkersSigner::create_deterministic_signer();
let era_tools = build_tools();
let _ = era_tools
.generate_tx_datum(Epoch(1), Some(Epoch(2)), &era_markers_signer)
.expect_err("generate_tx_datum should have failed");
}
}
2 changes: 2 additions & 0 deletions mithril-aggregator/src/tools/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
mod digest_helpers;
mod era;
mod genesis;
mod remote_file_uploader;

pub use digest_helpers::extract_digest_from_path;
pub use era::{EraTools, EraToolsDependency};
pub use genesis::{GenesisTools, GenesisToolsDependency};
pub use remote_file_uploader::{GcpFileUploader, RemoteFileUploader};

Expand Down
3 changes: 2 additions & 1 deletion mithril-common/src/entities/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ pub use signer::{Signer, SignerWithStake};
pub use single_signatures::SingleSignatures;
pub use snapshot::Snapshot;
pub use type_alias::{
HexEncodedAgregateVerificationKey, HexEncodedDigest, HexEncodedGenesisSecretKey,
HexEncodedAgregateVerificationKey, HexEncodedDigest, HexEncodedEraMarkersSecretKey,
HexEncodedEraMarkersSignature, HexEncodedEraMarkersVerificationKey, HexEncodedGenesisSecretKey,
HexEncodedGenesisSignature, HexEncodedGenesisVerificationKey, HexEncodedKey,
HexEncodedMultiSignature, HexEncodedOpCert, HexEncodedSingleSignature,
HexEncodedVerificationKey, HexEncodedVerificationKeySignature, ImmutableFileName,
Expand Down
9 changes: 9 additions & 0 deletions mithril-common/src/entities/type_alias.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,12 @@ pub type HexEncodedGenesisSignature = HexEncodedKey;

/// Hex encoded Sha256 Digest
pub type HexEncodedDigest = HexEncodedKey;

/// Hex encoded Era Markers Secret Key
pub type HexEncodedEraMarkersSecretKey = HexEncodedKey;

/// Hex encoded Era Markers Verification Key
pub type HexEncodedEraMarkersVerificationKey = HexEncodedKey;

/// Hex encoded Era Markers Signature
pub type HexEncodedEraMarkersSignature = HexEncodedKey;
11 changes: 6 additions & 5 deletions mithril-common/src/era/adapters/cardano_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{
key_decode_hex, EraMarkersSigner, EraMarkersVerifier, EraMarkersVerifierSignature,
EraMarkersVerifierVerificationKey,
},
entities::HexEncodedEraMarkersSignature,
era::{EraMarker, EraReaderAdapter},
};
use async_trait::async_trait;
Expand All @@ -15,8 +16,6 @@ use thiserror::Error;

type GeneralError = Box<dyn StdError + Sync + Send>;

type HexEncodeEraMarkerSignature = String;

/// [EraMarkersPayload] related errors.
#[derive(Debug, Error)]
pub enum EraMarkersPayloadError {
Expand Down Expand Up @@ -44,8 +43,11 @@ pub enum EraMarkersPayloadError {
/// Era markers payload
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct EraMarkersPayload {
markers: Vec<EraMarker>,
signature: Option<HexEncodeEraMarkerSignature>,
/// List of Era markers
pub markers: Vec<EraMarker>,

/// Era markers signature
pub signature: Option<HexEncodedEraMarkersSignature>,
}

impl EraMarkersPayload {
Expand Down Expand Up @@ -79,7 +81,6 @@ impl EraMarkersPayload {
}

/// Sign an era markers payload
#[allow(dead_code)]
pub fn sign(self, signer: &EraMarkersSigner) -> Result<Self, EraMarkersPayloadError> {
let signature = signer
.sign(
Expand Down
5 changes: 4 additions & 1 deletion mithril-common/src/era/adapters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ mod file;

pub use bootstrap::BootstrapAdapter as EraReaderBootstrapAdapter;
pub use builder::{AdapterBuilder as EraReaderAdapterBuilder, AdapterType as EraReaderAdapterType};
pub use cardano_chain::CardanoChainAdapter as EraReaderCardanoChainAdapter;
pub use cardano_chain::{
CardanoChainAdapter as EraReaderCardanoChainAdapter,
EraMarkersPayload as EraMarkersPayloadCardanoChain,
};
pub use dummy::DummyAdapter as EraReaderDummyAdapter;
pub use file::FileAdapter as EraReaderFileAdapter;
6 changes: 4 additions & 2 deletions mithril-common/src/era/supported_era.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use serde::Deserialize;
use serde::{Deserialize, Serialize};
use strum::IntoEnumIterator;
use strum_macros::{Display, EnumIter, EnumString};

/// Error related to [SupportedEra] String parsing implementation.
pub type UnsupportedEraError = strum::ParseError;

/// The era that the software is running or will run
#[derive(Display, EnumString, EnumIter, Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
#[derive(
Display, EnumString, EnumIter, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize,
)]
#[strum(serialize_all = "lowercase")]
pub enum SupportedEra {
/// Thales era
Expand Down

0 comments on commit ed6d680

Please sign in to comment.