Skip to content

Commit 2d2b544

Browse files
committed
Merge pull request #756 from input-output-hk/jpraynaud/755-era-cli-command
Add era command in aggregator cli
2 parents 87938ad + a325ca7 commit 2d2b544

File tree

16 files changed

+405
-554
lines changed

16 files changed

+405
-554
lines changed

.github/workflows/ci.yml

+13-463
Large diffs are not rendered by default.

Cargo.lock

+23-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mithril-aggregator/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mithril-aggregator"
3-
version = "0.2.19"
3+
version = "0.2.20"
44
description = "A Mithril Aggregator server"
55
authors = { workspace = true }
66
edition = { workspace = true }

mithril-aggregator/src/command_args.rs

+113-3
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,23 @@ use tokio::{sync::RwLock, time::Duration};
88
use mithril_common::{
99
certificate_chain::MithrilCertificateVerifier,
1010
chain_observer::{CardanoCliRunner, ChainObserver},
11-
crypto_helper::{key_decode_hex, ProtocolGenesisSigner, ProtocolGenesisVerifier},
11+
crypto_helper::{
12+
key_decode_hex, EraMarkersSigner, ProtocolGenesisSigner, ProtocolGenesisVerifier,
13+
},
1214
database::{ApplicationNodeType, DatabaseVersionChecker},
1315
digesters::{
1416
cache::{ImmutableFileDigestCacheProvider, JsonImmutableFileDigestCacheProviderBuilder},
1517
CardanoImmutableDigester, ImmutableFileSystemObserver,
1618
},
17-
entities::{Epoch, HexEncodedGenesisSecretKey},
19+
entities::{Epoch, HexEncodedEraMarkersSecretKey, HexEncodedGenesisSecretKey},
1820
era::{EraChecker, EraReader},
1921
store::{adapter::SQLiteAdapter, StakeStore},
2022
BeaconProvider, BeaconProviderImpl,
2123
};
2224

2325
use crate::{
2426
event_store::{self, TransmitterService},
25-
tools::{GenesisTools, GenesisToolsDependency},
27+
tools::{EraTools, GenesisTools, GenesisToolsDependency},
2628
AggregatorConfig, AggregatorRunner, AggregatorRuntime, CertificatePendingStore,
2729
CertificateStore, Configuration, DefaultConfiguration, DependencyManager, GenesisConfiguration,
2830
GzipSnapshotter, MithrilSignerRegisterer, MultiSignerImpl, ProtocolParametersStore,
@@ -228,6 +230,7 @@ impl MainOpts {
228230
#[derive(Debug, Clone, Subcommand)]
229231
pub enum MainCommand {
230232
Genesis(GenesisCommand),
233+
Era(EraCommand),
231234
Serve(ServeCommand),
232235
}
233236

@@ -238,6 +241,7 @@ impl MainCommand {
238241
) -> Result<(), Box<dyn Error>> {
239242
match self {
240243
Self::Genesis(cmd) => cmd.execute(config_builder).await,
244+
Self::Era(cmd) => cmd.execute(config_builder).await,
241245
Self::Serve(cmd) => cmd.execute(config_builder).await,
242246
}
243247
}
@@ -650,3 +654,109 @@ impl BootstrapGenesisSubCommand {
650654
.await
651655
}
652656
}
657+
658+
/// Era tools
659+
#[derive(Parser, Debug, Clone)]
660+
pub struct EraCommand {
661+
/// commands
662+
#[clap(subcommand)]
663+
pub era_subcommand: EraSubCommand,
664+
}
665+
666+
impl EraCommand {
667+
pub async fn execute(
668+
&self,
669+
config_builder: ConfigBuilder<DefaultState>,
670+
) -> Result<(), Box<dyn Error>> {
671+
self.era_subcommand.execute(config_builder).await
672+
}
673+
}
674+
675+
/// Era tools commands.
676+
#[derive(Debug, Clone, Subcommand)]
677+
pub enum EraSubCommand {
678+
/// Era list command.
679+
List(ListEraSubCommand),
680+
681+
/// Era tx datum generate command.
682+
GenerateTxDatum(GenerateTxDatumEraSubCommand),
683+
}
684+
685+
impl EraSubCommand {
686+
pub async fn execute(
687+
&self,
688+
config_builder: ConfigBuilder<DefaultState>,
689+
) -> Result<(), Box<dyn Error>> {
690+
match self {
691+
Self::List(cmd) => cmd.execute(config_builder).await,
692+
Self::GenerateTxDatum(cmd) => cmd.execute(config_builder).await,
693+
}
694+
}
695+
}
696+
697+
/// Era list command
698+
#[derive(Parser, Debug, Clone)]
699+
pub struct ListEraSubCommand {
700+
/// Enable JSON output.
701+
#[clap(long)]
702+
json: bool,
703+
}
704+
705+
impl ListEraSubCommand {
706+
pub async fn execute(
707+
&self,
708+
_config_builder: ConfigBuilder<DefaultState>,
709+
) -> Result<(), Box<dyn Error>> {
710+
debug!("LIST ERA command");
711+
let era_tools = EraTools::new();
712+
let eras = era_tools.get_supported_eras_list()?;
713+
714+
if self.json {
715+
println!("{}", serde_json::to_string(&eras)?);
716+
} else {
717+
println!("Supported Eras:");
718+
println!("{eras:#?}");
719+
}
720+
721+
Ok(())
722+
}
723+
}
724+
725+
/// Era tx datum generate command
726+
#[derive(Parser, Debug, Clone)]
727+
pub struct GenerateTxDatumEraSubCommand {
728+
/// Current Era epoch
729+
#[clap(long, env = "CURRENT_ERA_EPOCH")]
730+
current_era_epoch: u64,
731+
732+
/// Next Era epoch start, if exists
733+
#[clap(long, env = "NEXT_ERA_EPOCH")]
734+
next_era_epoch: Option<u64>,
735+
736+
/// Era Markers Secret Key
737+
#[clap(long, env = "ERA_MARKERS_SECRET_KEY")]
738+
era_markers_secret_key: HexEncodedEraMarkersSecretKey,
739+
}
740+
741+
impl GenerateTxDatumEraSubCommand {
742+
pub async fn execute(
743+
&self,
744+
_config_builder: ConfigBuilder<DefaultState>,
745+
) -> Result<(), Box<dyn Error>> {
746+
debug!("GENERATETXDATUM ERA command");
747+
let era_tools = EraTools::new();
748+
749+
let era_markers_secret_key = key_decode_hex(&self.era_markers_secret_key)?;
750+
let era_markers_signer = EraMarkersSigner::from_secret_key(era_markers_secret_key);
751+
print!(
752+
"{}",
753+
era_tools.generate_tx_datum(
754+
Epoch(self.current_era_epoch),
755+
self.next_era_epoch.map(Epoch),
756+
&era_markers_signer
757+
)?
758+
);
759+
760+
Ok(())
761+
}
762+
}

mithril-aggregator/src/tools/era.rs

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use std::error::Error;
2+
3+
use mithril_common::{
4+
chain_observer::{TxDatumBuilder, TxDatumFieldValue},
5+
crypto_helper::{key_encode_hex, EraMarkersSigner},
6+
entities::Epoch,
7+
era::{adapters::EraMarkersPayloadCardanoChain, EraMarker, SupportedEra},
8+
};
9+
10+
type EraToolsResult<R> = Result<R, Box<dyn Error>>;
11+
12+
pub struct EraTools {}
13+
14+
impl EraTools {
15+
pub fn new() -> Self {
16+
Self {}
17+
}
18+
19+
/// Get list of supported eras
20+
pub fn get_supported_eras_list(&self) -> EraToolsResult<Vec<SupportedEra>> {
21+
Ok(SupportedEra::eras())
22+
}
23+
24+
/// Generate TxDatum for eras with sanity check of epochs
25+
pub fn generate_tx_datum(
26+
&self,
27+
current_era_epoch: Epoch,
28+
maybe_next_era_epoch: Option<Epoch>,
29+
era_markers_signer: &EraMarkersSigner,
30+
) -> EraToolsResult<String> {
31+
if maybe_next_era_epoch.unwrap_or_default() >= current_era_epoch {
32+
Err("next era epoch must be strictly greater than the current era epoch".to_string())?;
33+
}
34+
35+
let mut era_markers = Vec::new();
36+
for (index, era) in SupportedEra::eras().iter().enumerate() {
37+
let era_marker = match index {
38+
0 => EraMarker::new(&era.to_string(), Some(current_era_epoch)),
39+
1 => EraMarker::new(&era.to_string(), maybe_next_era_epoch),
40+
_ => Err("too many eras retrieved, can't generate tx datum".to_string())?,
41+
};
42+
era_markers.push(era_marker);
43+
}
44+
let era_markers_payload = EraMarkersPayloadCardanoChain {
45+
markers: era_markers,
46+
signature: None,
47+
}
48+
.sign(era_markers_signer)?;
49+
50+
let tx_datum = TxDatumBuilder::new()
51+
.add_field(TxDatumFieldValue::Bytes(
52+
key_encode_hex(era_markers_payload)
53+
.map_err(|e| format!("era markerspayload could not be hex encoded: {e}"))?,
54+
))
55+
.build()?;
56+
57+
Ok(tx_datum.0)
58+
}
59+
}
60+
61+
#[cfg(test)]
62+
mod tests {
63+
use super::*;
64+
65+
fn build_tools() -> EraTools {
66+
EraTools {}
67+
}
68+
69+
#[test]
70+
fn get_supported_eras_list() {
71+
let era_tools = build_tools();
72+
let supported_eras_list = era_tools
73+
.get_supported_eras_list()
74+
.expect("get_supported_eras_list should not fail");
75+
assert_eq!(supported_eras_list, SupportedEra::eras());
76+
}
77+
78+
#[test]
79+
fn generate_tx_datum_ok() {
80+
let era_markers_signer = EraMarkersSigner::create_deterministic_signer();
81+
let era_tools = build_tools();
82+
let _ = era_tools
83+
.generate_tx_datum(Epoch(1), None, &era_markers_signer)
84+
.expect("generate_tx_datum should not fail");
85+
}
86+
87+
#[test]
88+
fn generate_tx_datum_wrong_epochs() {
89+
let era_markers_signer = EraMarkersSigner::create_deterministic_signer();
90+
let era_tools = build_tools();
91+
let _ = era_tools
92+
.generate_tx_datum(Epoch(1), Some(Epoch(2)), &era_markers_signer)
93+
.expect_err("generate_tx_datum should have failed");
94+
}
95+
}

mithril-aggregator/src/tools/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
mod digest_helpers;
2+
mod era;
23
mod genesis;
34
mod remote_file_uploader;
45

56
pub use digest_helpers::extract_digest_from_path;
7+
pub use era::EraTools;
68
pub use genesis::{GenesisTools, GenesisToolsDependency};
79
pub use remote_file_uploader::{GcpFileUploader, RemoteFileUploader};
810

mithril-common/Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mithril-common"
3-
version = "0.2.16"
3+
version = "0.2.17"
44
authors = { workspace = true }
55
edition = { workspace = true }
66
documentation = { workspace = true }
@@ -44,6 +44,8 @@ sha2 = "0.10.2"
4444
slog = { version = "2.7.0", features = ["max_level_trace", "release_max_level_debug"] }
4545
slog-scope = "4.4.0"
4646
sqlite = "0.28"
47+
strum = "0.24.1"
48+
strum_macros = "0.24.1"
4749
thiserror = "1.0.31"
4850
tokio = { version = "1.17.0", features = ["full"] }
4951
walkdir = "2"

mithril-common/src/chain_observer/cli_observer.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,7 @@ pool1qz2vzszautc2c8mljnqre2857dpmheq7kgt6vav0s38tvvhxm6w 1.051e-6
587587
let observer = CardanoCliChainObserver::new(Box::new(TestCliRunner {}));
588588
let address = "addrtest_123456".to_string();
589589
let datums = observer.get_current_datums(&address).await.unwrap();
590-
assert_eq!(vec![TxDatum("{\"constructor\":0,\"fields\":[{\"bytes\":\"5b0a20207b0a20202020226e616d65223a20227468616c6573222c0a202020202265706f6368223a203132330a20207d2c0a20207b0a20202020226e616d65223a20227079746861676f726173222c0a202020202265706f6368223a206e756c6c0a20207d0a5d0a\"}]}".to_string())], datums);
590+
assert_eq!(vec![TxDatum(r#"{"constructor":0,"fields":[{"bytes":"5b0a20207b0a20202020226e616d65223a20227468616c6573222c0a202020202265706f6368223a203132330a20207d2c0a20207b0a20202020226e616d65223a20227079746861676f726173222c0a202020202265706f6368223a206e756c6c0a20207d0a5d0a"}]}"#.to_string())], datums);
591591
}
592592

593593
#[tokio::test]

mithril-common/src/chain_observer/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@ pub use cli_observer::{CardanoCliChainObserver, CardanoCliRunner};
1010
#[cfg(any(test, feature = "test_only"))]
1111
pub use fake_observer::FakeObserver;
1212
pub use interface::{ChainObserver, ChainObserverError};
13-
pub use model::{ChainAddress, TxDatum};
13+
pub use model::{
14+
ChainAddress, TxDatum, TxDatumBuilder, TxDatumError, TxDatumFieldTypeName, TxDatumFieldValue,
15+
};

0 commit comments

Comments
 (0)