diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 626e448c8a0..164133e3d96 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -349,6 +349,14 @@ jobs: ] include: + # Include a test with a signer using HTTP adapter for protocol configurations (instead of on-chain by default) + - mode: "signer-legacy-http-protocol-configurations" + era: ${{ fromJSON(needs.build-ubuntu.outputs.eras)[0] }} + next_era: "" + cardano_node_version: "11.0.1" + hard_fork_latest_era_at_epoch: 0 + run_id: "#1" + extra_args: "--signer-protocol-configuration-reader-adapter=http --aggregate-signature-type=Concatenation full --check-client-cli-snapshot-converter" # Include a test for partial decentralization with leader/follower signer registration and P2P signature registration with Haskell DMQ node (without fallback signature delayer) - mode: "leader-follower-dmq-haskell-skip-delayer" era: ${{ fromJSON(needs.build-ubuntu.outputs.eras)[0] }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bf4d03b3a0..b63124459c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,16 +12,7 @@ As a minor extension, we have adopted a slightly different versioning convention - **UNSTABLE**: - Support for bytes encoding of the SNARK aggregate signatures in the certificates. - Reduced the encoded size of the SNARK proofs by serializing their bytes as CBOR byte strings. - -| Crate | Version | -| ------------------- | ------- | -| mithril-aggregator | `-` | -| mithril-client | `-` | -| mithril-client-cli | `-` | -| mithril-client-wasm | `-` | -| mithril-common | `-` | -| mithril-signer | `-` | -| mithril-stm | `-` | + - Support for reading on-chain protocol configurations for both Aggregator and Signer. - **UNSTABLE** Reworked the Mithril aggregator's file archiver to produce byte-stable archives across systems. - Existing archives must be regenerated by the Mithril aggregator to ensure byte stability. @@ -33,6 +24,16 @@ As a minor extension, we have adopted a slightly different versioning convention removal should not impact standard operations using current Mithril distributions. - Users relying on older `Gzip` snapshot artifacts or custom aggregators producing `Gzip` archives must migrate to `Zstandard`. +| Crate | Version | +| ------------------- | ------- | +| mithril-aggregator | `-` | +| mithril-client | `-` | +| mithril-client-cli | `-` | +| mithril-client-wasm | `-` | +| mithril-common | `-` | +| mithril-signer | `-` | +| mithril-stm | `-` | + ## Mithril Distribution [2630.0] - 2026-08-03 - **REMOVED** support for `CardanoImmutableFilesFull` in Mithril signer and aggregator: diff --git a/Cargo.lock b/Cargo.lock index 837aedcab60..3f8c08ed5b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4214,7 +4214,7 @@ dependencies = [ [[package]] name = "mithril-aggregator" -version = "0.9.22" +version = "0.10.0" dependencies = [ "anyhow", "async-trait", @@ -4359,7 +4359,7 @@ dependencies = [ [[package]] name = "mithril-cardano-node-chain" -version = "0.1.29" +version = "0.1.30" dependencies = [ "anyhow", "async-trait", @@ -4513,7 +4513,7 @@ dependencies = [ [[package]] name = "mithril-common" -version = "0.7.17" +version = "0.7.18" dependencies = [ "anyhow", "async-trait", @@ -4598,7 +4598,7 @@ dependencies = [ [[package]] name = "mithril-end-to-end" -version = "0.5.14" +version = "0.5.15" dependencies = [ "anyhow", "async-recursion", @@ -4705,7 +4705,7 @@ dependencies = [ [[package]] name = "mithril-protocol-config" -version = "0.1.13" +version = "0.1.14" dependencies = [ "anyhow", "async-trait", @@ -4788,7 +4788,7 @@ dependencies = [ [[package]] name = "mithril-signer" -version = "1.1.8" +version = "1.2.0" dependencies = [ "anyhow", "async-trait", diff --git a/internal/cardano-node/mithril-cardano-node-chain/Cargo.toml b/internal/cardano-node/mithril-cardano-node-chain/Cargo.toml index 6dc5b471ee9..2461a4a53a2 100644 --- a/internal/cardano-node/mithril-cardano-node-chain/Cargo.toml +++ b/internal/cardano-node/mithril-cardano-node-chain/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mithril-cardano-node-chain" -version = "0.1.29" +version = "0.1.30" authors.workspace = true documentation.workspace = true edition.workspace = true diff --git a/internal/cardano-node/mithril-cardano-node-chain/src/chain_observer/cli_observer.rs b/internal/cardano-node/mithril-cardano-node-chain/src/chain_observer/cli_observer.rs index 760de62f984..1e1479b4b7d 100644 --- a/internal/cardano-node/mithril-cardano-node-chain/src/chain_observer/cli_observer.rs +++ b/internal/cardano-node/mithril-cardano-node-chain/src/chain_observer/cli_observer.rs @@ -4,7 +4,7 @@ use hex::FromHex; use nom::IResult; use rand_core::RngCore; use serde_json::Value; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::fs; use std::path::PathBuf; use tokio::process::Command; @@ -515,7 +515,8 @@ impl ChainObserver for CardanoCliChainObserver { .launch_utxo(address) .await .map_err(ChainObserverError::General)?; - let v: HashMap = serde_json::from_str(&output) + + let v: BTreeMap = serde_json::from_str(&output) .with_context(|| format!("output was = '{output}'")) .map_err(ChainObserverError::InvalidContent)?; diff --git a/internal/cardano-node/mithril-cardano-node-internal-database/Cargo.toml b/internal/cardano-node/mithril-cardano-node-internal-database/Cargo.toml index 64d2de78777..b44b872a882 100644 --- a/internal/cardano-node/mithril-cardano-node-internal-database/Cargo.toml +++ b/internal/cardano-node/mithril-cardano-node-internal-database/Cargo.toml @@ -15,7 +15,7 @@ anyhow = { workspace = true } async-trait = { workspace = true } digest = { workspace = true } hex = { workspace = true } -mithril-common = { path = "../../../mithril-common", version = "0.7.17" } +mithril-common = { path = "../../../mithril-common", version = "0.7.18" } serde = { workspace = true } serde_json = { workspace = true } sha2 = "0.10.9" diff --git a/internal/mithril-aggregator-client/Cargo.toml b/internal/mithril-aggregator-client/Cargo.toml index 937ebb41d08..048776d8eac 100644 --- a/internal/mithril-aggregator-client/Cargo.toml +++ b/internal/mithril-aggregator-client/Cargo.toml @@ -13,7 +13,7 @@ include = ["**/*.rs", "Cargo.toml", "README.md"] [dependencies] anyhow = { workspace = true } async-trait = { workspace = true } -mithril-common = { path = "../../mithril-common", version = "0.7.17" } +mithril-common = { path = "../../mithril-common", version = "0.7.18" } reqwest = { workspace = true } semver = { workspace = true } serde = { workspace = true } diff --git a/internal/mithril-aggregator-discovery/Cargo.toml b/internal/mithril-aggregator-discovery/Cargo.toml index a7dd53617ef..43f6eff84c6 100644 --- a/internal/mithril-aggregator-discovery/Cargo.toml +++ b/internal/mithril-aggregator-discovery/Cargo.toml @@ -14,7 +14,7 @@ include = ["**/*.rs", "Cargo.toml", "README.md", ".gitignore"] anyhow = { workspace = true } async-trait = { workspace = true } mithril-aggregator-client = { path = "../mithril-aggregator-client", version = "0.2.4" } -mithril-common = { path = "../../mithril-common", version = "0.7.17" } +mithril-common = { path = "../../mithril-common", version = "0.7.18" } rand = { version = "0.10.2" } reqwest = { workspace = true } serde = { workspace = true } diff --git a/internal/mithril-protocol-config/Cargo.toml b/internal/mithril-protocol-config/Cargo.toml index 4a718c335a6..6fec8763f24 100644 --- a/internal/mithril-protocol-config/Cargo.toml +++ b/internal/mithril-protocol-config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mithril-protocol-config" -version = "0.1.13" +version = "0.1.14" description = "Configuraton parameters for Mithril network" authors = { workspace = true } edition = { workspace = true } diff --git a/internal/mithril-protocol-config/src/builder.rs b/internal/mithril-protocol-config/src/builder.rs new file mode 100644 index 00000000000..7ab401a7699 --- /dev/null +++ b/internal/mithril-protocol-config/src/builder.rs @@ -0,0 +1,157 @@ +//! Builder for creating a ProtocolConfigurationMarkersReader based on AdapterConfig. + +use std::{str::FromStr, sync::Arc}; + +use serde::Deserialize; + +use mithril_cardano_node_chain::{chain_observer::ChainObserver, entities::ChainAddress}; +use mithril_common::{ + crypto_helper::ProtocolConfigurationMarkersVerifierVerificationKey, + entities::ProtocolParameters, +}; + +use crate::{ + cardano_chain::protocol_configuration_reader::CardanoChainProtocolConfigurationMarkersReader, + interface::ProtocolConfigurationMarkersReader, + test::double::FakeProtocolConfigurationMarkersReader, +}; + +/// Configuration of Protocol Configuration Adapter +#[derive(Debug, Clone, Deserialize, PartialEq)] +#[serde(rename_all = "kebab-case", tag = "type")] +pub enum AdapterConfig { + /// Cardano chain protocol configuration adapter + CardanoChain { + /// Cardano chain address + address: ChainAddress, + + /// Verification key + verification_key: Box, + }, + + /// Fake protocol configuration adapter with given protocol parameters and default value, at epoch 0 (for test usage) + Fake { + /// Protocol parameters + protocol_parameters: ProtocolParameters, + }, +} + +impl FromStr for AdapterConfig { + type Err = serde_json::Error; + + fn from_str(s: &str) -> Result { + serde_json::from_str(s) + } +} + +/// Build a ProtocolConfigurationMarkersReader from configuration settings. +pub fn build_protocol_configuration_adapter( + adapter_config: AdapterConfig, + chain_observer: Arc, +) -> Arc { + match adapter_config { + AdapterConfig::CardanoChain { + address, + verification_key, + } => Arc::new(CardanoChainProtocolConfigurationMarkersReader::new( + address, + chain_observer, + *verification_key, + )), + AdapterConfig::Fake { + protocol_parameters, + } => Arc::new( + FakeProtocolConfigurationMarkersReader::default_with_protocol_parameters( + protocol_parameters, + ), + ), + } +} + +#[cfg(test)] +mod test { + + use super::*; + + static VERIFICATION_KEY: &str = "5b35352c3232382c3134342c38372c3133382c3133362c34382c382c31342c3138372c38352c3134382c39372c3233322c3235352c3232392c33382c3234342c3234372c3230342c3139382c31332c33312c3232322c32352c3136342c35322c3130322c39312c3132302c3230382c3134375d"; + + #[test] + fn deserialize_cardano_chain_adapter_config_from_json() { + let serialized_json = serde_json::json!({ + "type": "cardano-chain", + "address": "my_address", + "verification_key": VERIFICATION_KEY, + }); + + let payload = serde_json::to_string(&serialized_json).unwrap(); + let deserialized: AdapterConfig = serde_json::from_str(&payload).unwrap(); + + assert_eq!( + deserialized, + AdapterConfig::CardanoChain { + address: "my_address".to_string(), + verification_key: Box::new(VERIFICATION_KEY.try_into().expect("should not fail")) + } + ); + } + + #[test] + fn deserialize_fake_adapter_config_from_json() { + let serialized_json = serde_json::json!({ + "type": "fake", + "protocol_parameters": { + "k": 1944, + "m": 16948, + "phi_f": 0.2 + }, + }); + + let payload = serde_json::to_string(&serialized_json).unwrap(); + let deserialized: AdapterConfig = serde_json::from_str(&payload).unwrap(); + + assert_eq!( + deserialized, + AdapterConfig::Fake { + protocol_parameters: ProtocolParameters { + k: 1944, + m: 16948, + phi_f: 0.2 + } + } + ); + } + + #[test] + fn rejects_adapter_config_with_invalid_verification_key() { + let invalid_verification_key = "invalid_verification_key"; + + let serialized_json = serde_json::json!({ + "type": "cardano-chain", + "address": "my_address", + "verification_key": invalid_verification_key, + }); + + let payload = serde_json::to_string(&serialized_json).unwrap(); + let result = serde_json::from_str::(&payload); + assert!(result.is_err()) + } + + #[test] + fn rejects_adapter_config_with_unknown_type() { + let serialized_json = serde_json::json!({ + "type": "invalid-type", + "address": "A", + "verification_key": "B", + }); + + let payload = serde_json::to_string(&serialized_json).unwrap(); + let result = serde_json::from_str::(&payload); + assert!(result.is_err()); + assert!( + result + .unwrap_err() + .to_string() + .contains("unknown variant `invalid-type`") + ); + } +} diff --git a/internal/mithril-protocol-config/src/cardano_chain/message.rs b/internal/mithril-protocol-config/src/cardano_chain/message.rs index 69e0971e5a0..1b1bc28da98 100644 --- a/internal/mithril-protocol-config/src/cardano_chain/message.rs +++ b/internal/mithril-protocol-config/src/cardano_chain/message.rs @@ -16,6 +16,8 @@ use mithril_common::{ messages::SignedEntityTypeDiscriminantsMessage, }; +use crate::model::ProtocolConfigurationForEpoch; + /// The CBOR HEX representation of a [ProtocolConfigurationForEpochMessage] pub type CborHexProtocolConfigurationForEpochMessage = String; @@ -86,6 +88,16 @@ impl From for ProtocolParametersMessage { } } +impl From for ProtocolParameters { + fn from(params: ProtocolParametersMessage) -> Self { + ProtocolParameters { + k: params.k, + m: params.m, + phi_f: params.phi_f, + } + } +} + /// Configuration for the signing of Cardano transactions /// /// used for the CBOR HEX representation of [ProtocolConfigurationForEpochMessage] @@ -107,6 +119,15 @@ impl From for CardanoTransactionsSigningConfig } } +impl From for CardanoTransactionsSigningConfig { + fn from(config: CardanoTransactionsSigningConfigMessage) -> Self { + CardanoTransactionsSigningConfig { + security_parameter: config.security_parameter, + step: config.step, + } + } +} + /// Configuration for the signing of Cardano blocks and transactions /// /// used for the CBOR HEX representation of [ProtocolConfigurationForEpochMessage] @@ -130,7 +151,18 @@ impl From } } -//A epoch configuration used for the CBOR HEX representation in the [ProtocolConfigurationMarker] +impl From + for CardanoBlocksTransactionsSigningConfig +{ + fn from(config: CardanoBlocksTransactionsSigningConfigMessage) -> Self { + CardanoBlocksTransactionsSigningConfig { + security_parameter: config.security_parameter, + step: config.step, + } + } +} + +/// An epoch configuration used for the CBOR HEX representation in the [ProtocolConfigurationMarker] #[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] /// A network configuration available for an epoch pub struct ProtocolConfigurationForEpochMessage { @@ -189,6 +221,20 @@ impl ProtocolConfigurationForEpochMessage { } } +impl From for ProtocolConfigurationForEpoch { + fn from(message: ProtocolConfigurationForEpochMessage) -> Self { + ProtocolConfigurationForEpoch { + protocol_parameters: message.protocol_parameters.into(), + enabled_signed_entity_types: + SignedEntityTypeDiscriminantsMessage::into_known_discriminants( + message.enabled_signed_entity_types, + ), + cardano_transactions: message.cardano_transactions.map(Into::into), + cardano_blocks_transactions: message.cardano_blocks_transactions.map(Into::into), + } + } +} + #[cfg(test)] mod tests { use mithril_common::test::double::Dummy; diff --git a/internal/mithril-protocol-config/src/cardano_chain/payload.rs b/internal/mithril-protocol-config/src/cardano_chain/payload.rs index 379e6b5dce3..adea2c0b9f5 100644 --- a/internal/mithril-protocol-config/src/cardano_chain/payload.rs +++ b/internal/mithril-protocol-config/src/cardano_chain/payload.rs @@ -5,8 +5,9 @@ use serde::{Deserialize, Serialize}; use thiserror::Error; use mithril_common::crypto_helper::{ - ProtocolConfigurationMarkersSigner, ProtocolConfigurationMarkersVerifierSignature, - key_decode_hex, key_encode_hex, + ProtocolConfigurationMarkersSigner, ProtocolConfigurationMarkersVerifier, + ProtocolConfigurationMarkersVerifierSignature, + ProtocolConfigurationMarkersVerifierVerificationKey, key_decode_hex, key_encode_hex, }; use mithril_common::{StdError, StdResult}; @@ -38,6 +39,7 @@ pub enum ProtocolConfigurationMarkersPayloadError { /// Protocol Configuration markers payload #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(transparent)] pub struct ProtocolConfigurationMarkersPayload { /// List of protocol configuration markers pub markers: Vec, @@ -46,22 +48,15 @@ pub struct ProtocolConfigurationMarkersPayload { /// Signed Protocol Configuration markers payload #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct SignedProtocolConfigurationMarkersPayload { - /// List of protocol configuration markers - pub markers: Vec, + /// Protocol Configuration Markers Payload + #[serde(rename = "markers")] + pub markers_payload: ProtocolConfigurationMarkersPayload, /// Protocol Configuration markers signature pub signature: ProtocolConfigurationMarkersVerifierSignature, } impl SignedProtocolConfigurationMarkersPayload { - /// Instanciate a new SignedProtocolConfigurationMarkersPayload with markers and signature - pub fn new( - markers: Vec, - signature: ProtocolConfigurationMarkersVerifierSignature, - ) -> Self { - Self { markers, signature } - } - /// Encode this payload to a json hex string pub fn to_json_hex(&self) -> StdResult { key_encode_hex(self).with_context( @@ -75,6 +70,20 @@ impl SignedProtocolConfigurationMarkersPayload { || "SignedProtocolConfigurationMarkersPayload could not be decoded from json hex", ) } + + /// Verify the signature of a signed protocol configuration markers payload + pub fn verify_signature( + &self, + verification_key: ProtocolConfigurationMarkersVerifierVerificationKey, + ) -> Result<(), ProtocolConfigurationMarkersPayloadError> { + let markers_verifier: ProtocolConfigurationMarkersVerifier = + ProtocolConfigurationMarkersVerifier::from_verification_key(verification_key); + + markers_verifier + .verify(&self.markers_payload.message_to_bytes()?, &self.signature) + .with_context(|| "protocol configuration markers payload could not verify signature") + .map_err(ProtocolConfigurationMarkersPayloadError::VerifySignature) + } } impl ProtocolConfigurationMarkersPayload { @@ -100,7 +109,7 @@ impl ProtocolConfigurationMarkersPayload { })?); Ok(SignedProtocolConfigurationMarkersPayload { - markers: self.markers, + markers_payload: self, signature, }) } @@ -148,4 +157,43 @@ mod tests { assert_eq!(payload, payload_from_json_hex); } + + #[test] + fn verify_signature_is_ok() { + let markers = vec![ProtocolConfigurationMarker::new( + Epoch(42), + "cbor_protocol_configuration".to_string(), + )]; + let signer = ProtocolConfigurationMarkersSigner::create_deterministic_signer(); + + let payload = ProtocolConfigurationMarkersPayload::new(markers) + .sign(&signer) + .unwrap(); + + assert!(payload.verify_signature(signer.verification_key()).is_ok()); + } + + #[test] + fn verify_signature_fails_with_invalid_verification_key() { + let markers = vec![ProtocolConfigurationMarker::new( + Epoch(42), + "cbor_protocol_configuration".to_string(), + )]; + let signer = ProtocolConfigurationMarkersSigner::create_deterministic_signer(); + + let payload = ProtocolConfigurationMarkersPayload::new(markers) + .sign(&signer) + .unwrap(); + + let bad_signer = ProtocolConfigurationMarkersSigner::create_non_deterministic_signer(); + + let error = payload.verify_signature(bad_signer.verification_key()).unwrap_err(); + assert!( + matches!( + error, + ProtocolConfigurationMarkersPayloadError::VerifySignature(_) + ), + "unexpected error type: {error:?}" + ); + } } diff --git a/internal/mithril-protocol-config/src/cardano_chain/protocol_configuration_reader.rs b/internal/mithril-protocol-config/src/cardano_chain/protocol_configuration_reader.rs index 9968d459fe5..04505f5eac1 100644 --- a/internal/mithril-protocol-config/src/cardano_chain/protocol_configuration_reader.rs +++ b/internal/mithril-protocol-config/src/cardano_chain/protocol_configuration_reader.rs @@ -1,34 +1,41 @@ //! Cardano Chain implementation to read protocol configuration markers +use anyhow::Context; use async_trait::async_trait; +use std::collections::BTreeMap; use std::sync::Arc; use mithril_cardano_node_chain::chain_observer::ChainObserver; -use mithril_cardano_node_chain::entities::ChainAddress; +use mithril_cardano_node_chain::entities::{ChainAddress, TxDatumFieldTypeName}; use mithril_common::StdResult; use mithril_common::crypto_helper::ProtocolConfigurationMarkersVerifierVerificationKey; +use mithril_common::entities::Epoch; +use crate::cardano_chain::message::{ + ProtocolConfigurationForEpochMessage, ProtocolConfigurationMarker, +}; +use crate::cardano_chain::payload::SignedProtocolConfigurationMarkersPayload; use crate::interface::ProtocolConfigurationMarkersReader; -use crate::model::ConfigurationResolverFromMarkers; +use crate::model::{ConfigurationResolverFromMarkers, ProtocolConfigurationForEpoch}; /// Cardano Chain reader retrieves protocol configuration markers on chain pub struct CardanoChainProtocolConfigurationMarkersReader { - _address: ChainAddress, - _chain_observer: Arc, - _verification_key: ProtocolConfigurationMarkersVerifierVerificationKey, + address: ChainAddress, + chain_observer: Arc, + verification_key: ProtocolConfigurationMarkersVerifierVerificationKey, } impl CardanoChainProtocolConfigurationMarkersReader { /// CardanoChainAdapter factory pub fn new( - _address: ChainAddress, - _chain_observer: Arc, - _verification_key: ProtocolConfigurationMarkersVerifierVerificationKey, + address: ChainAddress, + chain_observer: Arc, + verification_key: ProtocolConfigurationMarkersVerifierVerificationKey, ) -> Self { Self { - _address, - _chain_observer, - _verification_key, + address, + chain_observer, + verification_key, } } } @@ -36,12 +43,167 @@ impl CardanoChainProtocolConfigurationMarkersReader { #[async_trait] impl ProtocolConfigurationMarkersReader for CardanoChainProtocolConfigurationMarkersReader { async fn read_configuration_markers(&self) -> StdResult { - //read payload - // to ProtocolConfigurationForEpochMessage - // to ProtocolConfigurationForEpoch - // build ConfigurationComputerFromMarkers with ProtocolConfigurationForEpoch - Err(anyhow::anyhow!( - "CardanoChainProtocolConfigurationMarkersReader::read is not implemented yet" - )) + let tx_datums = self.chain_observer.get_current_datums(&self.address).await?; + + let last_markers: Vec = tx_datums + .into_iter() + .filter_map(|datum| datum.get_fields_by_type(&TxDatumFieldTypeName::Bytes).ok()) + .map(|fields| { + fields + .iter() + .filter_map(|field_value| field_value.as_str().map(|s| s.to_string())) + .collect::>() + .join("") + }) + .filter_map(|field_value_str| { + SignedProtocolConfigurationMarkersPayload::from_json_hex(&field_value_str).ok() + }) + .filter_map(|signed_payload| { + signed_payload + .verify_signature(self.verification_key) + .ok() + .map(|_| signed_payload.markers_payload.markers) + }) + .next() + .unwrap_or_default(); + + let mut markers_map: BTreeMap = BTreeMap::new(); + for marker in last_markers { + markers_map.insert( + marker.epoch, + ProtocolConfigurationForEpochMessage::from_cbor_hex(&marker.configuration) + .with_context(|| { + format!( + "ProtocolConfigurationForEpochMessage for Epoch({}) could not be decoded from cbor hex", + marker.epoch, + ) + })? + .into(), + ); + } + + Ok(ConfigurationResolverFromMarkers::new(markers_map)) + } +} +#[cfg(test)] +mod test { + use mithril_cardano_node_chain::{ + entities::{TxDatum, TxDatumBuilder, TxDatumFieldValue}, + test::double::FakeChainObserver, + }; + use mithril_common::{crypto_helper::ProtocolConfigurationMarkersSigner, entities::Epoch}; + + use crate::cardano_chain::{ + ProtocolConfigurationMarkersPayloadCardanoChain, + SignedProtocolConfigurationMarkersPayloadCardanoChain, + message::ProtocolConfigurationMarker, + }; + + use crate::test::helper::{generate_configuration, generate_configuration_message}; + + use super::*; + + fn dummy_tx_datums_from_markers_payload( + payloads: Vec, + ) -> Vec { + payloads + .into_iter() + .map(|payload| { + TxDatumBuilder::new() + .add_field(TxDatumFieldValue::Bytes(payload.to_json_hex().unwrap())) + .build() + .unwrap() + }) + .collect() + } + + #[tokio::test] + async fn markers_with_invalid_signature_are_rejected() { + let conf_a = generate_configuration_message('A'); + let conf_b = generate_configuration_message('B'); + let conf_c = generate_configuration_message('C'); + let conf_d = generate_configuration_message('D'); + + let markers_signer = ProtocolConfigurationMarkersSigner::create_deterministic_signer(); + let bad_markers_signer = + ProtocolConfigurationMarkersSigner::create_non_deterministic_signer(); + + let fake_address = "addr_test_123456".to_string(); + let payload_1 = ProtocolConfigurationMarkersPayloadCardanoChain { + markers: vec![ + ProtocolConfigurationMarker::new(Epoch(41), conf_a.to_cbor_hex().unwrap()), + ProtocolConfigurationMarker::new(Epoch(42), conf_b.to_cbor_hex().unwrap()), + ], + } + .sign(&bad_markers_signer) + .unwrap(); + + let payload_2 = ProtocolConfigurationMarkersPayloadCardanoChain { + markers: vec![ + ProtocolConfigurationMarker::new(Epoch(43), conf_c.to_cbor_hex().unwrap()), + ProtocolConfigurationMarker::new(Epoch(44), conf_d.to_cbor_hex().unwrap()), + ], + } + .sign(&markers_signer) + .unwrap(); + + let mut fake_datums = dummy_tx_datums_from_markers_payload(vec![payload_2, payload_1]); + + fake_datums.push(TxDatum("not_valid_datum".to_string())); + + let chain_observer = FakeChainObserver::default(); + chain_observer.set_datums(fake_datums.clone()).await; + + let cardano_chain_reader = CardanoChainProtocolConfigurationMarkersReader::new( + fake_address, + Arc::new(chain_observer), + markers_signer.create_verifier().to_verification_key(), + ); + let configurations = cardano_chain_reader + .read_configuration_markers() + .await + .expect("read_configuration_markers should not fail"); + + assert_eq!(2, configurations.markers.len()); + assert_eq!( + Some(&generate_configuration('C')), + configurations.markers.get(&Epoch(43)) + ); + assert_eq!( + Some(&generate_configuration('D')), + configurations.markers.get(&Epoch(44)) + ); + } + + #[tokio::test] + async fn throw_error_on_invalid_cbor() { + let markers_signer = ProtocolConfigurationMarkersSigner::create_deterministic_signer(); + let fake_address = "addr_test_123456".to_string(); + let payload_1 = ProtocolConfigurationMarkersPayloadCardanoChain { + markers: vec![ + ProtocolConfigurationMarker::new( + Epoch(41), + generate_configuration_message('A').to_cbor_hex().unwrap(), + ), + ProtocolConfigurationMarker::new(Epoch(42), "unvalid_cbor_hex".to_string()), + ], + } + .sign(&markers_signer) + .unwrap(); + + let fake_datums = dummy_tx_datums_from_markers_payload(vec![payload_1]); + + let chain_observer = FakeChainObserver::default(); + chain_observer.set_datums(fake_datums.clone()).await; + + let cardano_chain_reader = CardanoChainProtocolConfigurationMarkersReader::new( + fake_address, + Arc::new(chain_observer), + markers_signer.create_verifier().to_verification_key(), + ); + cardano_chain_reader + .read_configuration_markers() + .await + .expect_err("should throw error"); } } diff --git a/internal/mithril-protocol-config/src/http.rs b/internal/mithril-protocol-config/src/http.rs index 36a1bbb3e67..1025fbf2044 100644 --- a/internal/mithril-protocol-config/src/http.rs +++ b/internal/mithril-protocol-config/src/http.rs @@ -8,7 +8,7 @@ use std::sync::Arc; use mithril_aggregator_client::AggregatorHttpClient; use mithril_aggregator_client::query::GetProtocolConfigurationQuery; use mithril_common::StdResult; -use mithril_common::entities::{Epoch, SignedEntityConfigValidator}; +use mithril_common::entities::{Epoch, InconsistentSignedEntityConfigError}; use mithril_common::logging::LoggerExtensions; use crate::interface::MithrilNetworkConfigurationProvider; @@ -33,28 +33,22 @@ impl HttpMithrilNetworkConfigurationProvider { &self, epoch: Epoch, ) -> StdResult> { - if let Some(mut configuration) = self + let configuration = self .aggregator_http_client .send(GetProtocolConfigurationQuery::epoch(epoch)) .await? - .map(Into::::into) - { - if let Err(err) = SignedEntityConfigValidator::check_consistency( - &configuration.enabled_signed_entity_types, - &configuration.signed_entity_types_config.cardano_transactions, - &configuration.signed_entity_types_config.cardano_blocks_transactions, - ) { - warn!( - self.logger, "Some allowed signed entity could not be enabled for epoch {epoch}; using only the usable subset"; - "error" => %err - ); - configuration.enabled_signed_entity_types = err.usable_discriminants; - }; - - Ok(Some(configuration)) - } else { - Ok(None) - } + .map(MithrilNetworkConfigurationForEpoch::from) + .map(|configuration| { + configuration.ensure_consistency(|err| self.log_inconsistency_error(epoch, err)) + }); + Ok(configuration) + } + + fn log_inconsistency_error(&self, epoch: Epoch, err: &InconsistentSignedEntityConfigError) { + warn!( + &self.logger, "Some allowed signed entity could not be enabled for epoch {epoch}; using only the usable subset"; + "error" => %err + ); } } diff --git a/internal/mithril-protocol-config/src/lib.rs b/internal/mithril-protocol-config/src/lib.rs index 88caae5f36c..e07a2d1471d 100644 --- a/internal/mithril-protocol-config/src/lib.rs +++ b/internal/mithril-protocol-config/src/lib.rs @@ -1,8 +1,10 @@ #![warn(missing_docs)] //! This crate provides mechanisms to read and check the configuration parameters of a Mithril network. +pub mod builder; pub mod cardano_chain; pub mod http; pub mod interface; +pub mod markers; pub mod model; pub mod test; diff --git a/internal/mithril-protocol-config/src/markers.rs b/internal/mithril-protocol-config/src/markers.rs new file mode 100644 index 00000000000..88651de865e --- /dev/null +++ b/internal/mithril-protocol-config/src/markers.rs @@ -0,0 +1,208 @@ +//! Markers implementation of MithrilNetworkConfigurationProvider. + +use std::sync::Arc; + +use anyhow::Context; +use async_trait::async_trait; +use mithril_common::{ + StdResult, + entities::{Epoch, InconsistentSignedEntityConfigError}, + logging::LoggerExtensions, +}; +use slog::{Logger, warn}; + +use crate::{ + interface::{MithrilNetworkConfigurationProvider, ProtocolConfigurationMarkersReader}, + model::{MithrilNetworkConfiguration, MithrilNetworkConfigurationForEpoch}, +}; + +/// Structure implementing MithrilNetworkConfigurationProvider using Cardano Chain. +pub struct MarkersMithrilNetworkConfigurationProvider { + logger: Logger, + markers_reader: Arc, +} + +impl MarkersMithrilNetworkConfigurationProvider { + /// MarkersMithrilNetworkConfigurationProvider factory + pub fn new( + logger: Logger, + markers_reader: Arc, + ) -> Self { + Self { + logger: logger.new_with_component_name::(), + markers_reader, + } + } + + fn log_inconsistency_error(&self, epoch: Epoch, err: &InconsistentSignedEntityConfigError) { + warn!( + &self.logger, "Some allowed signed entity could not be enabled; using only the usable subset"; + "epoch" => %epoch, + "error" => %err + ); + } +} + +#[async_trait] +impl MithrilNetworkConfigurationProvider for MarkersMithrilNetworkConfigurationProvider { + async fn get_network_configuration( + &self, + epoch: Epoch, + ) -> StdResult { + let aggregation_epoch = epoch.offset_to_signer_retrieval_epoch_saturating(); + let next_aggregation_epoch = epoch.offset_to_next_signer_retrieval_epoch(); + let registration_epoch = epoch.offset_to_next_signer_retrieval_epoch().next(); + + let configurations = self.markers_reader.read_configuration_markers().await?; + + let configuration_for_aggregation = configurations + .get_network_configuration(aggregation_epoch) + .map(MithrilNetworkConfigurationForEpoch::from) + .with_context(|| { + format!("Missing network configuration for aggregation epoch {aggregation_epoch}") + })? + .ensure_consistency(|err| self.log_inconsistency_error(aggregation_epoch, err)); + + let configuration_for_next_aggregation = configurations + .get_network_configuration(next_aggregation_epoch) + .map(MithrilNetworkConfigurationForEpoch::from) + .with_context(|| { + format!("Missing network configuration for next aggregation epoch {next_aggregation_epoch}") + })? + .ensure_consistency(|err| self.log_inconsistency_error(next_aggregation_epoch, err)); + + let configuration_for_registration = configurations + .get_network_configuration(registration_epoch) + .map(MithrilNetworkConfigurationForEpoch::from) + .with_context(|| { + format!("Missing network configuration for registration epoch {registration_epoch}") + })? + .ensure_consistency(|err| self.log_inconsistency_error(registration_epoch, err)); + + Ok(MithrilNetworkConfiguration { + epoch, + configuration_for_aggregation, + configuration_for_next_aggregation, + configuration_for_registration, + }) + } +} + +#[cfg(test)] +mod tests { + use std::collections::{BTreeMap, BTreeSet}; + + use mithril_common::entities::SignedEntityTypeDiscriminants; + use mithril_common::test::double::Dummy; + use mithril_common::test::entities_extensions::SignedEntityTypeDiscriminantsTestExtension; + + use crate::model::ProtocolConfigurationForEpoch; + use crate::{model::ConfigurationResolverFromMarkers, test::test_tools::TestLogger}; + + use crate::test::helper::generate_configuration; + + use super::*; + + fn build_markers_cardano_chain_network_provider( + logger: Logger, + configurations: ConfigurationResolverFromMarkers, + ) -> MarkersMithrilNetworkConfigurationProvider { + MarkersMithrilNetworkConfigurationProvider { + logger, + markers_reader: Arc::new( + crate::test::double::FakeProtocolConfigurationMarkersReader::from_markers( + configurations, + ), + ), + } + } + + #[tokio::test] + async fn get_network_configuration_retrieve_configurations_for_aggregation_next_aggregation_and_registration() + { + let markers = BTreeMap::from([ + (Epoch(31), generate_configuration('A')), + (Epoch(38), generate_configuration('B')), + ]); + + let configurations: ConfigurationResolverFromMarkers = + ConfigurationResolverFromMarkers::new(markers); + + let markers_mithril_configuration_provider = + build_markers_cardano_chain_network_provider(TestLogger::stdout(), configurations); + + let mithril_network_configuration = markers_mithril_configuration_provider + .get_network_configuration(Epoch(42)) + .await + .expect("should have configuration"); + + assert_eq!(mithril_network_configuration.epoch, Epoch(42)); + assert_eq!( + mithril_network_configuration.configuration_for_aggregation, + generate_configuration('B').into() + ); + assert_eq!( + mithril_network_configuration.configuration_for_next_aggregation, + generate_configuration('B').into() + ); + assert_eq!( + mithril_network_configuration.configuration_for_registration, + generate_configuration('B').into() + ); + } + + #[tokio::test] + async fn get_network_configuration_removes_unavailable_discriminants_when_config_missing() { + let configuration = ProtocolConfigurationForEpoch { + enabled_signed_entity_types: SignedEntityTypeDiscriminants::all_with_unstable() + .into_iter() + .collect(), + cardano_transactions: None, + cardano_blocks_transactions: None, + ..Dummy::dummy() + }; + + let markers = BTreeMap::from([(Epoch(31), configuration)]); + + let configurations: ConfigurationResolverFromMarkers = + ConfigurationResolverFromMarkers::new(markers); + + let markers_mithril_configuration_provider = + build_markers_cardano_chain_network_provider(TestLogger::stdout(), configurations); + + let mithril_network_configuration = markers_mithril_configuration_provider + .get_network_configuration(Epoch(42)) + .await + .expect("should have configuration"); + + let expected_discriminants = SignedEntityTypeDiscriminants::all() + // Discriminants without associated configuration should have been removed + .difference(&BTreeSet::from([ + SignedEntityTypeDiscriminants::CardanoTransactions, + SignedEntityTypeDiscriminants::CardanoBlocksTransactions, + ])) + .cloned() + .collect::>(); + + assert_eq!( + expected_discriminants, + mithril_network_configuration + .configuration_for_aggregation + .enabled_signed_entity_types + ); + + assert_eq!( + expected_discriminants, + mithril_network_configuration + .configuration_for_next_aggregation + .enabled_signed_entity_types + ); + + assert_eq!( + expected_discriminants, + mithril_network_configuration + .configuration_for_registration + .enabled_signed_entity_types + ); + } +} diff --git a/internal/mithril-protocol-config/src/model.rs b/internal/mithril-protocol-config/src/model.rs index eb9b226bbb5..eb0f31c47ab 100644 --- a/internal/mithril-protocol-config/src/model.rs +++ b/internal/mithril-protocol-config/src/model.rs @@ -5,7 +5,8 @@ use std::collections::{BTreeMap, BTreeSet}; use mithril_common::{ entities::{ CardanoBlocksTransactionsSigningConfig, CardanoTransactionsSigningConfig, Epoch, - ProtocolParameters, SignedEntityTypeDiscriminants, + InconsistentSignedEntityConfigError, ProtocolParameters, SignedEntityConfigValidator, + SignedEntityTypeDiscriminants, }, messages::{ProtocolConfigurationMessage, SignedEntityTypeDiscriminantsMessage}, }; @@ -50,6 +51,26 @@ pub struct MithrilNetworkConfigurationForEpoch { pub signed_entity_types_config: SignedEntityTypeConfiguration, } +impl MithrilNetworkConfigurationForEpoch { + /// Ensures signed-entity configuration is consistent + /// and clean if needed `enabled_signed_entity_types` with the validated usable subset. + pub(crate) fn ensure_consistency(mut self, consistency_error_inspector: F) -> Self + where + F: FnOnce(&InconsistentSignedEntityConfigError), + { + if let Err(err) = SignedEntityConfigValidator::check_consistency( + &self.enabled_signed_entity_types, + &self.signed_entity_types_config.cardano_transactions, + &self.signed_entity_types_config.cardano_blocks_transactions, + ) { + consistency_error_inspector(&err); + self.enabled_signed_entity_types = err.usable_discriminants; + } + + self + } +} + impl From for MithrilNetworkConfigurationForEpoch { fn from(message: ProtocolConfigurationMessage) -> Self { MithrilNetworkConfigurationForEpoch { @@ -82,6 +103,19 @@ pub struct ProtocolConfigurationForEpoch { pub cardano_blocks_transactions: Option, } +impl From for MithrilNetworkConfigurationForEpoch { + fn from(protocol_configuration: ProtocolConfigurationForEpoch) -> Self { + MithrilNetworkConfigurationForEpoch { + protocol_parameters: protocol_configuration.protocol_parameters, + enabled_signed_entity_types: protocol_configuration.enabled_signed_entity_types, + signed_entity_types_config: SignedEntityTypeConfiguration { + cardano_transactions: protocol_configuration.cardano_transactions, + cardano_blocks_transactions: protocol_configuration.cardano_blocks_transactions, + }, + } + } +} + /// Configuration containing markers by epoch #[derive(Default, PartialEq, Clone, Debug)] pub struct ConfigurationResolverFromMarkers { @@ -95,6 +129,13 @@ impl ConfigurationResolverFromMarkers { Self { markers } } + /// Create a new instance with a empty BTreeMap markers. + pub fn new_empty() -> Self { + Self { + markers: BTreeMap::new(), + } + } + /// resolve configuration for given Epoch pub fn get_network_configuration(&self, epoch: Epoch) -> Option { self.markers diff --git a/internal/mithril-protocol-config/src/test/double/fake_markers_reader.rs b/internal/mithril-protocol-config/src/test/double/fake_markers_reader.rs index fe84ce7dc1b..760bc969dc4 100644 --- a/internal/mithril-protocol-config/src/test/double/fake_markers_reader.rs +++ b/internal/mithril-protocol-config/src/test/double/fake_markers_reader.rs @@ -1,15 +1,24 @@ -use std::sync::RwLock; +use std::{ + collections::{BTreeMap, BTreeSet}, + sync::RwLock, +}; use async_trait::async_trait; -use mithril_common::StdResult; +use mithril_common::{ + StdResult, + entities::{ + BlockNumber, BlockNumberOffset, CardanoBlocksTransactionsSigningConfig, + CardanoTransactionsSigningConfig, Epoch, ProtocolParameters, SignedEntityTypeDiscriminants, + }, +}; use crate::{ - interface::ProtocolConfigurationMarkersReader, model::ConfigurationResolverFromMarkers, + interface::ProtocolConfigurationMarkersReader, + model::{ConfigurationResolverFromMarkers, ProtocolConfigurationForEpoch}, }; /// Dummy reader is intended to be used in a test environment (end to end test) -/// to simulate retreiving protocol configurations -#[derive(Default)] +/// to simulate retrieving protocol configurations pub struct FakeProtocolConfigurationMarkersReader { markers: RwLock, } @@ -28,6 +37,69 @@ impl FakeProtocolConfigurationMarkersReader { let mut my_markers = self.markers.write().unwrap(); *my_markers = markers; } + + /// Instantiate a default ProtocolConfigurationMarkersReader with given ProtocolParameters and default values + pub fn default_with_protocol_parameters(protocol_parameters: ProtocolParameters) -> Self { + let markers = BTreeMap::from([( + Epoch(0), + ProtocolConfigurationForEpoch { + protocol_parameters, + enabled_signed_entity_types: BTreeSet::from([ + SignedEntityTypeDiscriminants::MithrilStakeDistribution, + SignedEntityTypeDiscriminants::CardanoStakeDistribution, + SignedEntityTypeDiscriminants::CardanoDatabase, + SignedEntityTypeDiscriminants::CardanoTransactions, + SignedEntityTypeDiscriminants::CardanoBlocksTransactions, + ]), + cardano_transactions: Some(CardanoTransactionsSigningConfig { + security_parameter: BlockNumberOffset(120), + step: BlockNumber(15), + }), + cardano_blocks_transactions: Some(CardanoBlocksTransactionsSigningConfig { + security_parameter: BlockNumberOffset(120), + step: BlockNumber(15), + }), + }, + )]); + + FakeProtocolConfigurationMarkersReader { + markers: RwLock::new(ConfigurationResolverFromMarkers::new(markers)), + } + } +} + +impl Default for FakeProtocolConfigurationMarkersReader { + fn default() -> Self { + let markers = BTreeMap::from([( + Epoch(0), + ProtocolConfigurationForEpoch { + protocol_parameters: ProtocolParameters { + k: 5, + m: 100, + phi_f: 0.95, + }, + enabled_signed_entity_types: BTreeSet::from([ + SignedEntityTypeDiscriminants::MithrilStakeDistribution, + SignedEntityTypeDiscriminants::CardanoStakeDistribution, + SignedEntityTypeDiscriminants::CardanoDatabase, + SignedEntityTypeDiscriminants::CardanoTransactions, + SignedEntityTypeDiscriminants::CardanoBlocksTransactions, + ]), + cardano_transactions: Some(CardanoTransactionsSigningConfig { + security_parameter: BlockNumberOffset(120), + step: BlockNumber(15), + }), + cardano_blocks_transactions: Some(CardanoBlocksTransactionsSigningConfig { + security_parameter: BlockNumberOffset(120), + step: BlockNumber(15), + }), + }, + )]); + + FakeProtocolConfigurationMarkersReader { + markers: RwLock::new(ConfigurationResolverFromMarkers::new(markers)), + } + } } #[async_trait] @@ -48,6 +120,8 @@ mod tests { #[tokio::test] async fn empty_dummy_reader() { let reader = FakeProtocolConfigurationMarkersReader::default(); + let markers = ConfigurationResolverFromMarkers::new_empty(); + reader.set_markers(markers); let result = reader .read_configuration_markers() diff --git a/internal/mithril-protocol-config/src/test/helper.rs b/internal/mithril-protocol-config/src/test/helper.rs new file mode 100644 index 00000000000..9301e8e9fa2 --- /dev/null +++ b/internal/mithril-protocol-config/src/test/helper.rs @@ -0,0 +1,73 @@ +//! Helper to generate configurations for tests + +use std::collections::BTreeSet; + +use mithril_common::{ + entities::{ + BlockNumber, BlockNumberOffset, CardanoBlocksTransactionsSigningConfig, + CardanoTransactionsSigningConfig, ProtocolParameters, + SignedEntityTypeDiscriminants::{ + self, CardanoBlocksTransactions, CardanoDatabase, CardanoStakeDistribution, + CardanoTransactions, + }, + }, + messages::SignedEntityTypeDiscriminantsMessage, +}; + +use crate::{ + cardano_chain::message::{ + CardanoBlocksTransactionsSigningConfigMessage, CardanoTransactionsSigningConfigMessage, + ProtocolConfigurationForEpochMessage, ProtocolParametersMessage, + }, + model::ProtocolConfigurationForEpoch, +}; + +/// Instantiate a unique [ProtocolConfigurationForEpochMessage] based on the given character +pub fn generate_configuration_message(conf: char) -> ProtocolConfigurationForEpochMessage { + ProtocolConfigurationForEpochMessage { + protocol_parameters: ProtocolParametersMessage { + k: conf as u64, + m: conf as u64, + phi_f: 0.2, + }, + cardano_transactions: Some(CardanoTransactionsSigningConfigMessage { + security_parameter: BlockNumberOffset(10), + step: BlockNumber(20), + }), + cardano_blocks_transactions: Some(CardanoBlocksTransactionsSigningConfigMessage { + security_parameter: BlockNumberOffset(30), + step: BlockNumber(40), + }), + enabled_signed_entity_types: BTreeSet::from([ + SignedEntityTypeDiscriminantsMessage::Known(CardanoTransactions), + SignedEntityTypeDiscriminantsMessage::Known(CardanoBlocksTransactions), + SignedEntityTypeDiscriminantsMessage::Known(CardanoDatabase), + SignedEntityTypeDiscriminantsMessage::Known(CardanoStakeDistribution), + ]), + } +} + +/// Instantiate a unique ProtocolConfigurationForEpoch based on char +pub fn generate_configuration(conf: char) -> ProtocolConfigurationForEpoch { + ProtocolConfigurationForEpoch { + protocol_parameters: ProtocolParameters { + k: conf as u64, + m: conf as u64, + phi_f: 0.2, + }, + cardano_transactions: Some(CardanoTransactionsSigningConfig { + security_parameter: BlockNumberOffset(10), + step: BlockNumber(20), + }), + cardano_blocks_transactions: Some(CardanoBlocksTransactionsSigningConfig { + security_parameter: BlockNumberOffset(30), + step: BlockNumber(40), + }), + enabled_signed_entity_types: BTreeSet::from([ + SignedEntityTypeDiscriminants::CardanoTransactions, + SignedEntityTypeDiscriminants::CardanoBlocksTransactions, + SignedEntityTypeDiscriminants::CardanoDatabase, + SignedEntityTypeDiscriminants::CardanoStakeDistribution, + ]), + } +} diff --git a/internal/mithril-protocol-config/src/test/mod.rs b/internal/mithril-protocol-config/src/test/mod.rs index fbcddda1f57..f25952301b9 100644 --- a/internal/mithril-protocol-config/src/test/mod.rs +++ b/internal/mithril-protocol-config/src/test/mod.rs @@ -5,6 +5,7 @@ //! This module provides in particular test doubles for the traits defined in this crate. pub mod double; +pub mod helper; #[cfg(test)] pub(crate) mod test_tools { diff --git a/mithril-aggregator/Cargo.toml b/mithril-aggregator/Cargo.toml index c0f4d754556..06cbc7d8abe 100644 --- a/mithril-aggregator/Cargo.toml +++ b/mithril-aggregator/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mithril-aggregator" -version = "0.9.22" +version = "0.10.0" description = "A Mithril Aggregator server" authors = { workspace = true } edition = { workspace = true } diff --git a/mithril-aggregator/src/commands/protocol_configuration_command.rs b/mithril-aggregator/src/commands/protocol_configuration_command.rs index 4c36ace8553..fe400bdc1cd 100644 --- a/mithril-aggregator/src/commands/protocol_configuration_command.rs +++ b/mithril-aggregator/src/commands/protocol_configuration_command.rs @@ -29,13 +29,13 @@ use mithril_common::{ messages::SignedEntityTypeDiscriminantsMessage::Known, }; use mithril_doc::{Documenter, StructDoc}; -use mithril_protocol_config::model::{ - ConfigurationResolverFromMarkers, ProtocolConfigurationForEpoch, +use mithril_protocol_config::{ + builder::AdapterConfig, + model::{ConfigurationResolverFromMarkers, ProtocolConfigurationForEpoch}, }; use crate::{ - ConfigurationSource, ExecutionEnvironment, - configuration::ProtocolConfigurationReaderParameters, extract_all, + ConfigurationSource, ExecutionEnvironment, extract_all, tools::HumanReadableProtocolConfiguration, }; use crate::{dependency_injection::DependenciesBuilder, tools::ProtocolConfigurationTools}; @@ -56,6 +56,12 @@ pub struct ProtocolConfigurationParametersConfiguration { #[example = "`/ipc/node.socket`"] pub cardano_node_socket_path: PathBuf, + /// Cardano Network Magic number + /// + /// useful for TestNet & DevNet + #[example = "`1097911063` or `42`"] + pub network_magic: Option, + /// Cardano network #[example = "`mainnet` or `preprod` or `devnet`"] network: String, @@ -63,12 +69,15 @@ pub struct ProtocolConfigurationParametersConfiguration { /// Cardano chain observer type pub chain_observer_type: ChainObserverType, - /// Protocol configuration Reader Adapter Parameters + /// Protocol configuration reader adapter configuration #[example = "\ - `{ \"address\": \"address\", \"verification_key\": \"key\" }`\ + - cardano-chain:
`{ \"type\": \"cardano-chain\", \"address\": \"test_address\", \"verification_key\": \"136372c3138312c3138382c3130352c3233312c3135\" }`
\ "] - #[serde(deserialize_with = "serde_deserialization::string_or_struct")] - pub protocol_configuration_reader_adapter_params: ProtocolConfigurationReaderParameters, + #[serde( + default, + deserialize_with = "serde_deserialization::string_or_struct_optional" + )] + pub protocol_configuration_reader_adapter_config: Option, } impl ConfigurationSource for ProtocolConfigurationParametersConfiguration { @@ -80,6 +89,10 @@ impl ConfigurationSource for ProtocolConfigurationParametersConfiguration { self.cardano_node_socket_path.clone() } + fn network_magic(&self) -> Option { + self.network_magic + } + fn network(&self) -> String { self.network.clone() } @@ -88,8 +101,8 @@ impl ConfigurationSource for ProtocolConfigurationParametersConfiguration { self.chain_observer_type.clone() } - fn protocol_configuration_reader_parameters(&self) -> ProtocolConfigurationReaderParameters { - self.protocol_configuration_reader_adapter_params.clone() + fn protocol_configuration_reader_adapter_config(&self) -> Option { + self.protocol_configuration_reader_adapter_config.clone() } } @@ -268,7 +281,7 @@ pub struct ImportProtocolConfigurationSubCommand { pub target_path: PathBuf, /// Protocol Configuration Markers Secret Key - #[clap(long, env = "PROTOCOL_CONFIGURATION_MARKERS_SECRET_KEY")] + #[clap(long, env = "PROTOCOL_CONFIGURATION_READER_SECRET_KEY")] protocol_configuration_markers_secret_key: HexEncodedProtocolConfigurationMarkersSecretKey, } diff --git a/mithril-aggregator/src/configuration.rs b/mithril-aggregator/src/configuration.rs index 3eb89039aca..4c9d7af2df4 100644 --- a/mithril-aggregator/src/configuration.rs +++ b/mithril-aggregator/src/configuration.rs @@ -9,9 +9,7 @@ use serde::Deserialize; use mithril_cardano_node_chain::chain_observer::ChainObserverType; use mithril_cli_helper::{register_config_value, serde_deserialization}; -use mithril_common::crypto_helper::{ - GenesisEd25519Signer, ManifestSigner, ProtocolConfigurationMarkersVerifierVerificationKey, -}; +use mithril_common::crypto_helper::{GenesisEd25519Signer, ManifestSigner}; use mithril_common::entities::{ BlockNumber, BlockNumberOffset, CardanoBlocksTransactionsSigningConfig, CardanoTransactionsSigningConfig, CompressionAlgorithm, ConfigSecret, @@ -23,6 +21,7 @@ use mithril_dmq::DmqNetwork; use mithril_doc::{Documenter, DocumenterDefault, StructDoc}; use mithril_era::adapters::EraReaderAdapterType; use mithril_file_archiver::ZstandardCompressionParameters; +use mithril_protocol_config::builder::AdapterConfig; use crate::entities::AggregatorEpochSettings; use crate::http_server::SERVER_BASE_PATH; @@ -199,9 +198,9 @@ pub trait ConfigurationSource { panic!("era_reader_adapter_params is not implemented."); } - /// Protocol configuration reader adapter parameters - fn protocol_configuration_reader_parameters(&self) -> ProtocolConfigurationReaderParameters { - panic!("protocol_configuration_reader_parameters is not implemented."); + /// Protocol configuration reader adapter configuration + fn protocol_configuration_reader_adapter_config(&self) -> Option { + panic!("protocol_configuration_reader_adapter_config is not implemented."); } /// Configuration of the ancillary files signer @@ -579,6 +578,16 @@ pub struct ServeCommandConfiguration { /// Era reader adapter parameters pub era_reader_adapter_params: Option, + /// Protocol configuration reader adapter configuration + #[example = "\ + - cardano-chain:
`{ \"type\": \"cardano-chain\", \"address\": \"test_address\", \"verification_key\": \"136372c3138312c3138382c3130352c3233312c3135\" }`
\ + "] + #[serde( + default, + deserialize_with = "serde_deserialization::string_or_struct_optional" + )] + pub protocol_configuration_reader_adapter_config: Option, + /// Configuration of the ancillary files signer /// /// Can either be a secret key or a key stored in a Google Cloud Platform KMS account. @@ -761,24 +770,6 @@ impl FromStr for AncillaryFilesSignerConfig { } } -/// Configuration of the protocol Configuration Reader -#[derive(Debug, Clone, Deserialize, PartialEq, Eq)] -pub struct ProtocolConfigurationReaderParameters { - /// Address - pub address: String, - - /// Verification key - pub verification_key: ProtocolConfigurationMarkersVerifierVerificationKey, -} - -impl FromStr for ProtocolConfigurationReaderParameters { - type Err = serde_json::Error; - - fn from_str(s: &str) -> Result { - serde_json::from_str(s) - } -} - impl ServeCommandConfiguration { /// Create a sample configuration mainly for tests pub fn new_sample(tmp_path: PathBuf) -> Self { @@ -798,11 +789,7 @@ impl ServeCommandConfiguration { network_magic: Some(42), dmq_network_magic: Some(3141592), chain_observer_type: ChainObserverType::Fake, - protocol_parameters: Some(ProtocolParameters { - k: 5, - m: 100, - phi_f: 0.95, - }), + protocol_parameters: None, snapshot_uploader_type: SnapshotUploaderType::Local, snapshot_bucket_name: None, snapshot_use_cdn_domain: false, @@ -825,6 +812,13 @@ impl ServeCommandConfiguration { store_retention_limit: None, era_reader_adapter_type: EraReaderAdapterType::Bootstrap, era_reader_adapter_params: None, + protocol_configuration_reader_adapter_config: Some(AdapterConfig::Fake { + protocol_parameters: ProtocolParameters { + k: 5, + m: 100, + phi_f: 0.95, + }, + }), ancillary_files_signer_config: AncillaryFilesSignerConfig::SecretKey { secret_key: ancillary_files_signer_secret_key.to_json_hex().unwrap(), }, @@ -838,16 +832,8 @@ impl ServeCommandConfiguration { cardano_blocks_transactions_database_connection_pool_size: 5, cardano_transactions_prover_cache_pool_size: 3, cardano_transactions_database_connection_pool_size: 5, - cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig { - security_parameter: BlockNumberOffset(120), - step: BlockNumber(15), - }), - cardano_blocks_transactions_signing_config: Some( - CardanoBlocksTransactionsSigningConfig { - security_parameter: BlockNumberOffset(120), - step: BlockNumber(15), - }, - ), + cardano_transactions_signing_config: None, + cardano_blocks_transactions_signing_config: None, preload_security_parameter: BlockNumber(30), cardano_prover_max_hashes_allowed_by_request: 100, cardano_transactions_block_streamer_max_roll_forwards_per_poll: 1000, @@ -977,6 +963,10 @@ impl ConfigurationSource for ServeCommandConfiguration { self.era_reader_adapter_params.clone() } + fn protocol_configuration_reader_adapter_config(&self) -> Option { + self.protocol_configuration_reader_adapter_config.clone() + } + fn ancillary_files_signer_config(&self) -> AncillaryFilesSignerConfig { self.ancillary_files_signer_config.clone() } diff --git a/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs b/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs index f980411c846..0fea3c2214c 100644 --- a/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs +++ b/mithril-aggregator/src/dependency_injection/builder/enablers/epoch.rs @@ -2,14 +2,17 @@ use std::sync::Arc; use tokio::sync::RwLock; use mithril_protocol_config::{ - http::HttpMithrilNetworkConfigurationProvider, interface::MithrilNetworkConfigurationProvider, + builder::build_protocol_configuration_adapter, interface::MithrilNetworkConfigurationProvider, + interface::ProtocolConfigurationMarkersReader, + markers::MarkersMithrilNetworkConfigurationProvider, + test::double::FakeProtocolConfigurationMarkersReader, }; -use crate::dependency_injection::{DependenciesBuilder, EpochServiceWrapper, Result}; -use crate::get_dependency; -use crate::services::{ - EpochServiceDependencies, LocalMithrilNetworkConfigurationProvider, MithrilEpochService, +use crate::dependency_injection::{ + DependenciesBuilder, DependenciesBuilderError, EpochServiceWrapper, Result, }; +use crate::services::{EpochServiceDependencies, MithrilEpochService}; +use crate::{ExecutionEnvironment, get_dependency}; impl DependenciesBuilder { async fn build_epoch_service(&mut self) -> Result { @@ -46,24 +49,10 @@ impl DependenciesBuilder { async fn build_mithril_network_configuration_provider( &mut self, ) -> Result> { - let network_configuration_provider: Arc = - if self.configuration.is_follower_aggregator() { - Arc::new(HttpMithrilNetworkConfigurationProvider::new( - self.get_leader_aggregator_client().await?, - self.root_logger(), - )) - } else { - Arc::new(LocalMithrilNetworkConfigurationProvider::new( - self.configuration - .get_leader_aggregator_epoch_settings_configuration()?, - self.configuration - .compute_allowed_signed_entity_types_discriminants()?, - self.get_epoch_settings_store().await?, - self.root_logger(), - )) - }; - - Ok(network_configuration_provider) + Ok(Arc::new(MarkersMithrilNetworkConfigurationProvider::new( + self.root_logger(), + self.get_protocol_configuration_reader().await?, + ))) } /// [MithrilNetworkConfigurationProvider][mithril_protocol_config::interface::MithrilNetworkConfigurationProvider] service @@ -72,4 +61,37 @@ impl DependenciesBuilder { ) -> Result> { get_dependency!(self.mithril_network_configuration_provider) } + + async fn build_protocol_configuration_reader( + &mut self, + ) -> Result> { + let protocol_configuration_markers_reader: Arc = + match self.configuration.environment() { + ExecutionEnvironment::Production => { + let adapter_config = self + .configuration + .protocol_configuration_reader_adapter_config() + .ok_or_else(|| { + DependenciesBuilderError::MissingConfiguration( + "protocol_configuration_reader_adapter_config".to_string(), + ) + })?; + + build_protocol_configuration_adapter( + adapter_config, + self.get_chain_observer().await?, + ) + } + _ => Arc::new(FakeProtocolConfigurationMarkersReader::default()), + }; + + Ok(protocol_configuration_markers_reader) + } + + /// [ProtocolConfigurationMarkersReader] service + pub async fn get_protocol_configuration_reader( + &mut self, + ) -> Result> { + get_dependency!(self.protocol_configuration_reader) + } } diff --git a/mithril-aggregator/src/dependency_injection/builder/support/compatibility.rs b/mithril-aggregator/src/dependency_injection/builder/support/compatibility.rs index ddfb71c5936..9c25a9ae75b 100644 --- a/mithril-aggregator/src/dependency_injection/builder/support/compatibility.rs +++ b/mithril-aggregator/src/dependency_injection/builder/support/compatibility.rs @@ -2,13 +2,8 @@ use std::sync::Arc; use mithril_common::api_version::APIVersionProvider; use mithril_common::entities::{Epoch, SupportedEra}; -use mithril_common::test::double::Dummy; use mithril_era::adapters::{EraReaderAdapterBuilder, EraReaderDummyAdapter}; use mithril_era::{EraChecker, EraMarker, EraReader, EraReaderAdapter}; -use mithril_protocol_config::cardano_chain::protocol_configuration_reader::CardanoChainProtocolConfigurationMarkersReader; -use mithril_protocol_config::interface::ProtocolConfigurationMarkersReader; -use mithril_protocol_config::model::ConfigurationResolverFromMarkers; -use mithril_protocol_config::test::double::FakeProtocolConfigurationMarkersReader; use crate::ExecutionEnvironment; use crate::dependency_injection::{DependenciesBuilder, DependenciesBuilderError, Result}; @@ -87,32 +82,4 @@ impl DependenciesBuilder { pub async fn get_era_checker(&mut self) -> Result> { get_dependency!(self.era_checker) } - - async fn build_protocol_configuration_reader( - &mut self, - ) -> Result> { - let protocol_configuration_markers_reader: Arc = - match self.configuration.environment() { - ExecutionEnvironment::Production => { - let parameters = self.configuration.protocol_configuration_reader_parameters(); - Arc::new(CardanoChainProtocolConfigurationMarkersReader::new( - parameters.address, - self.get_chain_observer().await?, - parameters.verification_key, - )) - } - _ => Arc::new(FakeProtocolConfigurationMarkersReader::from_markers( - ConfigurationResolverFromMarkers::dummy(), - )), - }; - - Ok(protocol_configuration_markers_reader) - } - - /// [ProtocolConfigurationMarkersReader] service - pub async fn get_protocol_configuration_reader( - &mut self, - ) -> Result> { - get_dependency!(self.protocol_configuration_reader) - } } diff --git a/mithril-aggregator/tests/cardano_stake_distribution_verify_stakes.rs b/mithril-aggregator/tests/cardano_stake_distribution_verify_stakes.rs index c84829f307a..6912ddc8a3a 100644 --- a/mithril-aggregator/tests/cardano_stake_distribution_verify_stakes.rs +++ b/mithril-aggregator/tests/cardano_stake_distribution_verify_stakes.rs @@ -1,5 +1,7 @@ mod test_extensions; +use std::collections::{BTreeMap, BTreeSet}; + use mithril_aggregator::ServeCommandConfiguration; use mithril_common::entities::SignerWithStake; use mithril_common::temp_dir; @@ -11,6 +13,9 @@ use mithril_common::{ }, test::builder::MithrilFixtureBuilder, }; +use mithril_protocol_config::model::{ + ConfigurationResolverFromMarkers, ProtocolConfigurationForEpoch, +}; use test_extensions::ExpectedMetrics; use test_extensions::{ExpectedCertificate, RuntimeTester, utilities::get_test_dir}; @@ -22,13 +27,23 @@ async fn cardano_stake_distribution_verify_stakes() { phi_f: 0.95, }; let configuration = ServeCommandConfiguration { - protocol_parameters: Some(protocol_parameters.clone()), signed_entity_types: Some( SignedEntityTypeDiscriminants::CardanoStakeDistribution.to_string(), ), data_stores_directory: get_test_dir("cardano_stake_distribution_verify_stakes"), ..ServeCommandConfiguration::new_sample(temp_dir!()) }; + let protocol_configuration_markers = ConfigurationResolverFromMarkers::new(BTreeMap::from([( + Epoch(0), + ProtocolConfigurationForEpoch { + protocol_parameters: protocol_parameters.clone(), + enabled_signed_entity_types: BTreeSet::from([ + SignedEntityTypeDiscriminants::CardanoStakeDistribution, + ]), + cardano_blocks_transactions: None, + cardano_transactions: None, + }, + )])); let mut tester = RuntimeTester::build( TimePoint::new( 2, @@ -36,6 +51,7 @@ async fn cardano_stake_distribution_verify_stakes() { ChainPoint::new(SlotNumber(10), BlockNumber(1), "block_hash-1"), ), configuration, + protocol_configuration_markers, ) .await; diff --git a/mithril-aggregator/tests/certificate_chain.rs b/mithril-aggregator/tests/certificate_chain.rs index 7376374dd2e..00f47fa922b 100644 --- a/mithril-aggregator/tests/certificate_chain.rs +++ b/mithril-aggregator/tests/certificate_chain.rs @@ -1,5 +1,7 @@ mod test_extensions; +use std::collections::{BTreeMap, BTreeSet}; + use mithril_aggregator::ServeCommandConfiguration; use mithril_common::{ entities::{ @@ -10,6 +12,9 @@ use mithril_common::{ temp_dir, test::builder::MithrilFixtureBuilder, }; +use mithril_protocol_config::model::{ + ConfigurationResolverFromMarkers, ProtocolConfigurationForEpoch, +}; use test_extensions::{ ExpectedCertificate, ExpectedMetrics, RuntimeTester, utilities::get_test_dir, }; @@ -22,11 +27,21 @@ async fn certificate_chain() { phi_f: 0.95, }; let configuration = ServeCommandConfiguration { - protocol_parameters: Some(protocol_parameters.clone()), data_stores_directory: get_test_dir("certificate_chain"), signed_entity_types: Some(SignedEntityTypeDiscriminants::CardanoDatabase.to_string()), ..ServeCommandConfiguration::new_sample(temp_dir!()) }; + let protocol_configuration_markers = ConfigurationResolverFromMarkers::new(BTreeMap::from([( + Epoch(0), + ProtocolConfigurationForEpoch { + protocol_parameters: protocol_parameters.clone(), + enabled_signed_entity_types: BTreeSet::from([ + SignedEntityTypeDiscriminants::CardanoDatabase, + ]), + cardano_blocks_transactions: None, + cardano_transactions: None, + }, + )])); let mut tester = RuntimeTester::build( TimePoint::new( 1, @@ -34,6 +49,7 @@ async fn certificate_chain() { ChainPoint::new(SlotNumber(10), BlockNumber(1), "block_hash-1"), ), configuration, + protocol_configuration_markers, ) .await; let observer = tester.observer.clone(); diff --git a/mithril-aggregator/tests/create_certificate.rs b/mithril-aggregator/tests/create_certificate.rs index 7233d0596d8..fb6a115cd71 100644 --- a/mithril-aggregator/tests/create_certificate.rs +++ b/mithril-aggregator/tests/create_certificate.rs @@ -1,5 +1,7 @@ mod test_extensions; +use std::collections::{BTreeMap, BTreeSet}; + use mithril_aggregator::ServeCommandConfiguration; use mithril_common::{ entities::{ @@ -11,6 +13,9 @@ use mithril_common::{ test::builder::MithrilFixtureBuilder, }; +use mithril_protocol_config::model::{ + ConfigurationResolverFromMarkers, ProtocolConfigurationForEpoch, +}; use test_extensions::{ ExpectedCertificate, ExpectedMetrics, RuntimeTester, utilities::get_test_dir, }; @@ -23,7 +28,6 @@ async fn create_certificate() { phi_f: 0.95, }; let configuration = ServeCommandConfiguration { - protocol_parameters: Some(protocol_parameters.clone()), signed_entity_types: Some( [ SignedEntityTypeDiscriminants::CardanoTransactions.to_string(), @@ -33,16 +37,27 @@ async fn create_certificate() { .join(","), ), data_stores_directory: get_test_dir("create_certificate"), - cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig { - security_parameter: BlockNumberOffset(0), - step: BlockNumber(30), - }), - cardano_blocks_transactions_signing_config: Some(CardanoBlocksTransactionsSigningConfig { - security_parameter: BlockNumberOffset(0), - step: BlockNumber(24), - }), ..ServeCommandConfiguration::new_sample(temp_dir!()) }; + let protocol_configuration_markers = ConfigurationResolverFromMarkers::new(BTreeMap::from([( + Epoch(0), + ProtocolConfigurationForEpoch { + protocol_parameters: protocol_parameters.clone(), + enabled_signed_entity_types: BTreeSet::from([ + SignedEntityTypeDiscriminants::CardanoTransactions, + SignedEntityTypeDiscriminants::CardanoBlocksTransactions, + SignedEntityTypeDiscriminants::CardanoDatabase, + ]), + cardano_transactions: Some(CardanoTransactionsSigningConfig { + security_parameter: BlockNumberOffset(0), + step: BlockNumber(30), + }), + cardano_blocks_transactions: Some(CardanoBlocksTransactionsSigningConfig { + security_parameter: BlockNumberOffset(0), + step: BlockNumber(24), + }), + }, + )])); let mut tester = RuntimeTester::build( TimePoint { epoch: Epoch(1), @@ -54,6 +69,7 @@ async fn create_certificate() { }, }, configuration, + protocol_configuration_markers, ) .await; diff --git a/mithril-aggregator/tests/create_certificate_follower.rs b/mithril-aggregator/tests/create_certificate_follower.rs index 1f361e95c5c..dbdce8429d0 100644 --- a/mithril-aggregator/tests/create_certificate_follower.rs +++ b/mithril-aggregator/tests/create_certificate_follower.rs @@ -1,6 +1,9 @@ mod test_extensions; -use std::{collections::HashMap, ops::Range}; +use std::{ + collections::{BTreeMap, BTreeSet, HashMap}, + ops::Range, +}; use mithril_aggregator::ServeCommandConfiguration; use mithril_common::{ @@ -14,6 +17,9 @@ use mithril_common::{ builder::{MithrilFixture, MithrilFixtureBuilder, StakeDistributionGenerationMethod}, }, }; +use mithril_protocol_config::model::{ + ConfigurationResolverFromMarkers, ProtocolConfigurationForEpoch, +}; use test_extensions::{ExpectedCertificate, RuntimeTester, utilities::get_test_dir}; /// Epoch fixtures helps using the fixtures in the tests @@ -98,15 +104,29 @@ async fn create_certificate_follower() { }, }; let leader_configuration = ServeCommandConfiguration { - protocol_parameters: Some(protocol_parameters.clone()), data_stores_directory: get_test_dir("create_certificate_leader"), signed_entity_types: Some( SignedEntityTypeDiscriminants::CardanoStakeDistribution.to_string(), ), ..ServeCommandConfiguration::new_sample(temp_dir!()) }; - let mut leader_tester = - RuntimeTester::build(start_time_point.clone(), leader_configuration.clone()).await; + let protocol_configuration_markers = ConfigurationResolverFromMarkers::new(BTreeMap::from([( + Epoch(0), + ProtocolConfigurationForEpoch { + protocol_parameters: protocol_parameters.clone(), + enabled_signed_entity_types: BTreeSet::from([ + SignedEntityTypeDiscriminants::CardanoStakeDistribution, + ]), + cardano_blocks_transactions: None, + cardano_transactions: None, + }, + )])); + let mut leader_tester = RuntimeTester::build( + start_time_point.clone(), + leader_configuration.clone(), + protocol_configuration_markers.clone(), + ) + .await; let leader_aggregator_http_server = leader_tester.spawn_leader_aggregator_http_server().await.unwrap(); @@ -117,12 +137,14 @@ async fn create_certificate_follower() { "create_certificate_follower", ), leader_aggregator_endpoint: Some(leader_aggregator_http_server.url().to_string()), - // Follower must retrieve parameters from the network configuration (today through the leader) - // so this parameters should not be read - protocol_parameters: None, ..leader_configuration }; - let mut follower_tester = RuntimeTester::build(start_time_point, follower_configuration).await; + let mut follower_tester = RuntimeTester::build( + start_time_point, + follower_configuration, + protocol_configuration_markers, + ) + .await; comment!( "Epoch 1: diff --git a/mithril-aggregator/tests/create_certificate_with_buffered_signatures.rs b/mithril-aggregator/tests/create_certificate_with_buffered_signatures.rs index 395b4655775..84c1a6bfe03 100644 --- a/mithril-aggregator/tests/create_certificate_with_buffered_signatures.rs +++ b/mithril-aggregator/tests/create_certificate_with_buffered_signatures.rs @@ -1,5 +1,7 @@ mod test_extensions; +use std::collections::{BTreeMap, BTreeSet}; + use mithril_aggregator::ServeCommandConfiguration; use mithril_common::{ entities::{ @@ -10,6 +12,9 @@ use mithril_common::{ temp_dir, test::builder::MithrilFixtureBuilder, }; +use mithril_protocol_config::model::{ + ConfigurationResolverFromMarkers, ProtocolConfigurationForEpoch, +}; use test_extensions::{ ExpectedCertificate, ExpectedMetrics, RuntimeTester, utilities::get_test_dir, }; @@ -22,15 +27,24 @@ async fn create_certificate_with_buffered_signatures() { phi_f: 0.95, }; let configuration = ServeCommandConfiguration { - protocol_parameters: Some(protocol_parameters.clone()), signed_entity_types: Some(SignedEntityTypeDiscriminants::CardanoTransactions.to_string()), data_stores_directory: get_test_dir("create_certificate_with_buffered_signatures"), - cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig { - security_parameter: BlockNumberOffset(0), - step: BlockNumber(30), - }), ..ServeCommandConfiguration::new_sample(temp_dir!()) }; + let protocol_configuration_markers = ConfigurationResolverFromMarkers::new(BTreeMap::from([( + Epoch(0), + ProtocolConfigurationForEpoch { + protocol_parameters: protocol_parameters.clone(), + enabled_signed_entity_types: BTreeSet::from([ + SignedEntityTypeDiscriminants::CardanoTransactions, + ]), + cardano_transactions: Some(CardanoTransactionsSigningConfig { + security_parameter: BlockNumberOffset(0), + step: BlockNumber(30), + }), + cardano_blocks_transactions: None, + }, + )])); let mut tester = RuntimeTester::build( TimePoint { epoch: Epoch(1), @@ -42,6 +56,7 @@ async fn create_certificate_with_buffered_signatures() { }, }, configuration, + protocol_configuration_markers, ) .await; diff --git a/mithril-aggregator/tests/era_checker.rs b/mithril-aggregator/tests/era_checker.rs index 49f276394be..d09ec6b43ec 100644 --- a/mithril-aggregator/tests/era_checker.rs +++ b/mithril-aggregator/tests/era_checker.rs @@ -1,15 +1,21 @@ mod test_extensions; +use std::collections::{BTreeMap, BTreeSet}; + use mithril_aggregator::{RuntimeError, ServeCommandConfiguration}; use mithril_common::{ entities::{ - BlockNumber, ChainPoint, Epoch, ProtocolParameters, SlotNumber, SupportedEra, TimePoint, + BlockNumber, ChainPoint, Epoch, ProtocolParameters, SignedEntityTypeDiscriminants, + SlotNumber, SupportedEra, TimePoint, }, temp_dir, test::{builder::MithrilFixtureBuilder, double::Dummy}, }; use mithril_era::EraMarker; +use mithril_protocol_config::model::{ + ConfigurationResolverFromMarkers, ProtocolConfigurationForEpoch, +}; use test_extensions::{RuntimeTester, utilities::get_test_dir}; #[tokio::test] @@ -20,10 +26,20 @@ async fn testing_eras() { phi_f: 0.95, }; let configuration = ServeCommandConfiguration { - protocol_parameters: Some(protocol_parameters.clone()), data_stores_directory: get_test_dir("testing_eras"), ..ServeCommandConfiguration::new_sample(temp_dir!()) }; + let protocol_configuration_markers = ConfigurationResolverFromMarkers::new(BTreeMap::from([( + Epoch(0), + ProtocolConfigurationForEpoch { + protocol_parameters: protocol_parameters.clone(), + enabled_signed_entity_types: BTreeSet::from([ + SignedEntityTypeDiscriminants::MithrilStakeDistribution, + ]), + cardano_blocks_transactions: None, + cardano_transactions: None, + }, + )])); let mut tester = RuntimeTester::build( TimePoint::new( 1, @@ -31,6 +47,7 @@ async fn testing_eras() { ChainPoint::new(SlotNumber(10), BlockNumber(1), "block_hash-1"), ), configuration, + protocol_configuration_markers, ) .await; tester.era_reader_adapter.set_markers(vec![ diff --git a/mithril-aggregator/tests/genesis_to_signing.rs b/mithril-aggregator/tests/genesis_to_signing.rs index e6de8fd5815..407c3d85158 100644 --- a/mithril-aggregator/tests/genesis_to_signing.rs +++ b/mithril-aggregator/tests/genesis_to_signing.rs @@ -1,5 +1,7 @@ mod test_extensions; +use std::collections::BTreeMap; + use mithril_aggregator::ServeCommandConfiguration; use mithril_common::{ entities::{ @@ -12,6 +14,9 @@ use mithril_common::{ entities_extensions::SignedEntityTypeDiscriminantsTestExtension, }, }; +use mithril_protocol_config::model::{ + ConfigurationResolverFromMarkers, ProtocolConfigurationForEpoch, +}; use test_extensions::{ExpectedCertificate, RuntimeTester, utilities::get_test_dir}; #[tokio::test] @@ -22,11 +27,29 @@ async fn genesis_to_signing_with_all_signed_entities() { phi_f: 0.65, }; let configuration = ServeCommandConfiguration { - protocol_parameters: Some(protocol_parameters.clone()), data_stores_directory: get_test_dir("genesis_to_signing_with_all_signed_entities"), signed_entity_types: Some(SignedEntityTypeDiscriminants::all_with_unstable_string(",")), ..ServeCommandConfiguration::new_sample(temp_dir!()) }; + let protocol_configuration_markers = ConfigurationResolverFromMarkers::new(BTreeMap::from([( + Epoch(0), + ProtocolConfigurationForEpoch { + protocol_parameters: protocol_parameters.clone(), + enabled_signed_entity_types: SignedEntityTypeDiscriminants::all_with_unstable(), + cardano_blocks_transactions: Some( + mithril_common::entities::CardanoBlocksTransactionsSigningConfig { + security_parameter: mithril_common::entities::BlockNumberOffset(120), + step: BlockNumber(15), + }, + ), + cardano_transactions: Some( + mithril_common::entities::CardanoTransactionsSigningConfig { + security_parameter: mithril_common::entities::BlockNumberOffset(120), + step: BlockNumber(15), + }, + ), + }, + )])); let mut tester = RuntimeTester::build( TimePoint::new( 1, @@ -34,6 +57,7 @@ async fn genesis_to_signing_with_all_signed_entities() { ChainPoint::new(SlotNumber(10), BlockNumber(1), "block_hash-1"), ), configuration, + protocol_configuration_markers, ) .await; diff --git a/mithril-aggregator/tests/leader_signed_entity_config_propagation.rs b/mithril-aggregator/tests/leader_signed_entity_config_propagation.rs index d02a8665977..bc21119fe3f 100644 --- a/mithril-aggregator/tests/leader_signed_entity_config_propagation.rs +++ b/mithril-aggregator/tests/leader_signed_entity_config_propagation.rs @@ -1,5 +1,7 @@ mod test_extensions; +use std::collections::{BTreeMap, BTreeSet}; + use mithril_aggregator::ServeCommandConfiguration; use mithril_cardano_node_chain::chain_observer::ChainObserver; use mithril_common::{ @@ -12,7 +14,9 @@ use mithril_common::{ temp_dir, test::builder::{MithrilFixture, MithrilFixtureBuilder}, }; -use std::collections::BTreeSet; +use mithril_protocol_config::model::{ + ConfigurationResolverFromMarkers, ProtocolConfigurationForEpoch, +}; use test_extensions::{ExpectedMetrics, RuntimeTester, utilities::get_test_dir}; #[tokio::test] @@ -34,7 +38,6 @@ async fn leader_signed_entity_config_propagation() { // First, start a runtime without the CardanoTransactions and CardanoBlocksTransactions signed entity // types but their configuration set to store them in the database and diffuse them. let start_configuration = ServeCommandConfiguration { - protocol_parameters: Some(protocol_parameters.clone()), signed_entity_types: Some( allowed_discriminants_at_start .iter() @@ -43,16 +46,23 @@ async fn leader_signed_entity_config_propagation() { .join(","), ), data_stores_directory: get_test_dir(current_function!()), - cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig { - security_parameter: BlockNumberOffset(0), - step: BlockNumber(30), - }), - cardano_blocks_transactions_signing_config: Some(CardanoBlocksTransactionsSigningConfig { - security_parameter: BlockNumberOffset(0), - step: BlockNumber(30), - }), ..ServeCommandConfiguration::new_sample(temp_dir!()) }; + let protocol_configuration_markers = ConfigurationResolverFromMarkers::new(BTreeMap::from([( + Epoch(0), + ProtocolConfigurationForEpoch { + protocol_parameters: protocol_parameters.clone(), + enabled_signed_entity_types: SignedEntityTypeDiscriminants::all(), + cardano_transactions: Some(CardanoTransactionsSigningConfig { + security_parameter: BlockNumberOffset(0), + step: BlockNumber(30), + }), + cardano_blocks_transactions: Some(CardanoBlocksTransactionsSigningConfig { + security_parameter: BlockNumberOffset(0), + step: BlockNumber(30), + }), + }, + )])); let mut tester = RuntimeTester::build( TimePoint { epoch: Epoch(1), @@ -64,6 +74,7 @@ async fn leader_signed_entity_config_propagation() { }, }, start_configuration.clone(), + protocol_configuration_markers.clone(), ) .await; @@ -94,16 +105,19 @@ async fn leader_signed_entity_config_propagation() { "restart the runtime state machine but with all remaining signed entity types enabled and configured" ); tester - .rebuild(ServeCommandConfiguration { - signed_entity_types: Some( - SignedEntityTypeDiscriminants::all() - .iter() - .map(|d| d.to_string()) - .collect::>() - .join(","), - ), - ..start_configuration - }) + .rebuild( + ServeCommandConfiguration { + signed_entity_types: Some( + SignedEntityTypeDiscriminants::all() + .iter() + .map(|d| d.to_string()) + .collect::>() + .join(","), + ), + ..start_configuration + }, + protocol_configuration_markers, + ) .await; cycle!(tester, "ready"); cycle!(tester, "signing"); diff --git a/mithril-aggregator/tests/open_message_expiration.rs b/mithril-aggregator/tests/open_message_expiration.rs index adc7b099606..7daac8a4d11 100644 --- a/mithril-aggregator/tests/open_message_expiration.rs +++ b/mithril-aggregator/tests/open_message_expiration.rs @@ -1,6 +1,9 @@ mod test_extensions; -use std::time::Duration; +use std::{ + collections::{BTreeMap, BTreeSet}, + time::Duration, +}; use mithril_aggregator::ServeCommandConfiguration; use mithril_common::{ @@ -11,6 +14,9 @@ use mithril_common::{ temp_dir, test::builder::MithrilFixtureBuilder, }; +use mithril_protocol_config::model::{ + ConfigurationResolverFromMarkers, ProtocolConfigurationForEpoch, +}; use test_extensions::{ ExpectedCertificate, ExpectedMetrics, RuntimeTester, utilities::get_test_dir, }; @@ -23,11 +29,21 @@ async fn open_message_expiration() { phi_f: 0.95, }; let configuration = ServeCommandConfiguration { - protocol_parameters: Some(protocol_parameters.clone()), data_stores_directory: get_test_dir("open_message_expiration"), signed_entity_types: Some(SignedEntityTypeDiscriminants::CardanoDatabase.to_string()), ..ServeCommandConfiguration::new_sample(temp_dir!()) }; + let protocol_configuration_markers = ConfigurationResolverFromMarkers::new(BTreeMap::from([( + Epoch(0), + ProtocolConfigurationForEpoch { + protocol_parameters: protocol_parameters.clone(), + enabled_signed_entity_types: BTreeSet::from([ + SignedEntityTypeDiscriminants::CardanoDatabase, + ]), + cardano_blocks_transactions: None, + cardano_transactions: None, + }, + )])); let mut tester = RuntimeTester::build( TimePoint::new( 1, @@ -35,6 +51,7 @@ async fn open_message_expiration() { ChainPoint::new(SlotNumber(10), BlockNumber(1), "block_hash-1"), ), configuration, + protocol_configuration_markers, ) .await; diff --git a/mithril-aggregator/tests/open_message_newer_exists.rs b/mithril-aggregator/tests/open_message_newer_exists.rs index 58ba834b961..9965689b2f8 100644 --- a/mithril-aggregator/tests/open_message_newer_exists.rs +++ b/mithril-aggregator/tests/open_message_newer_exists.rs @@ -1,5 +1,7 @@ mod test_extensions; +use std::collections::{BTreeMap, BTreeSet}; + use mithril_aggregator::ServeCommandConfiguration; use mithril_common::{ entities::{ @@ -9,6 +11,9 @@ use mithril_common::{ temp_dir, test::builder::MithrilFixtureBuilder, }; +use mithril_protocol_config::model::{ + ConfigurationResolverFromMarkers, ProtocolConfigurationForEpoch, +}; use test_extensions::{ ExpectedCertificate, ExpectedMetrics, RuntimeTester, utilities::get_test_dir, }; @@ -21,11 +26,21 @@ async fn open_message_newer_exists() { phi_f: 0.95, }; let configuration = ServeCommandConfiguration { - protocol_parameters: Some(protocol_parameters.clone()), data_stores_directory: get_test_dir("open_message_newer_exists"), signed_entity_types: Some(SignedEntityTypeDiscriminants::CardanoDatabase.to_string()), ..ServeCommandConfiguration::new_sample(temp_dir!()) }; + let protocol_configuration_markers = ConfigurationResolverFromMarkers::new(BTreeMap::from([( + Epoch(0), + ProtocolConfigurationForEpoch { + protocol_parameters: protocol_parameters.clone(), + enabled_signed_entity_types: BTreeSet::from([ + SignedEntityTypeDiscriminants::CardanoDatabase, + ]), + cardano_blocks_transactions: None, + cardano_transactions: None, + }, + )])); let mut tester = RuntimeTester::build( TimePoint::new( 1, @@ -33,6 +48,7 @@ async fn open_message_newer_exists() { ChainPoint::new(SlotNumber(10), BlockNumber(1), "block_hash-1"), ), configuration, + protocol_configuration_markers, ) .await; diff --git a/mithril-aggregator/tests/prove_blocks_transactions.rs b/mithril-aggregator/tests/prove_blocks_transactions.rs index 9edcfa7f560..8be4bd5c5f3 100644 --- a/mithril-aggregator/tests/prove_blocks_transactions.rs +++ b/mithril-aggregator/tests/prove_blocks_transactions.rs @@ -1,4 +1,7 @@ -use std::sync::Arc; +use std::{ + collections::{BTreeMap, BTreeSet}, + sync::Arc, +}; use mithril_aggregator::{ServeCommandConfiguration, services::ProverService}; use mithril_common::{ @@ -10,6 +13,9 @@ use mithril_common::{ temp_dir, test::{builder::MithrilFixtureBuilder, entities_extensions::BlockRangeTestExtension}, }; +use mithril_protocol_config::model::{ + ConfigurationResolverFromMarkers, ProtocolConfigurationForEpoch, +}; use test_extensions::{ ExpectedCertificate, ExpectedMetrics, RuntimeTester, utilities::get_test_dir, }; @@ -31,17 +37,26 @@ async fn prove_blocks_transactions() { smaller than one block range (5 blocks)" ); let configuration = ServeCommandConfiguration { - protocol_parameters: Some(protocol_parameters.clone()), signed_entity_types: Some( SignedEntityTypeDiscriminants::CardanoBlocksTransactions.to_string(), ), data_stores_directory: get_test_dir("prove_blocks_transactions"), - cardano_blocks_transactions_signing_config: Some(CardanoBlocksTransactionsSigningConfig { - security_parameter: BlockNumberOffset(0), - step: BlockNumber(5), - }), ..ServeCommandConfiguration::new_sample(temp_dir!()) }; + let protocol_configuration_markers = ConfigurationResolverFromMarkers::new(BTreeMap::from([( + Epoch(0), + ProtocolConfigurationForEpoch { + protocol_parameters: protocol_parameters.clone(), + enabled_signed_entity_types: BTreeSet::from([ + SignedEntityTypeDiscriminants::CardanoBlocksTransactions, + ]), + cardano_transactions: None, + cardano_blocks_transactions: Some(CardanoBlocksTransactionsSigningConfig { + security_parameter: BlockNumberOffset(0), + step: BlockNumber(5), + }), + }, + )])); let mut tester = RuntimeTester::build( TimePoint { epoch: Epoch(1), @@ -53,6 +68,7 @@ async fn prove_blocks_transactions() { }, }, configuration, + protocol_configuration_markers, ) .await; let observer = tester.observer.clone(); diff --git a/mithril-aggregator/tests/prove_transactions.rs b/mithril-aggregator/tests/prove_transactions.rs index 0a2c3ef5857..97e268c73d7 100644 --- a/mithril-aggregator/tests/prove_transactions.rs +++ b/mithril-aggregator/tests/prove_transactions.rs @@ -1,3 +1,5 @@ +use std::collections::{BTreeMap, BTreeSet}; + use mithril_aggregator::ServeCommandConfiguration; use mithril_common::{ entities::{ @@ -8,6 +10,9 @@ use mithril_common::{ temp_dir, test::builder::MithrilFixtureBuilder, }; +use mithril_protocol_config::model::{ + ConfigurationResolverFromMarkers, ProtocolConfigurationForEpoch, +}; use test_extensions::{ ExpectedCertificate, ExpectedMetrics, RuntimeTester, utilities::get_test_dir, }; @@ -24,15 +29,24 @@ async fn prove_transactions() { phi_f: 0.95, }; let configuration = ServeCommandConfiguration { - protocol_parameters: Some(protocol_parameters.clone()), signed_entity_types: Some(SignedEntityTypeDiscriminants::CardanoTransactions.to_string()), data_stores_directory: get_test_dir("prove_transactions"), - cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig { - security_parameter: BlockNumberOffset(0), - step: BlockNumber(30), - }), ..ServeCommandConfiguration::new_sample(temp_dir!()) }; + let protocol_configuration_markers = ConfigurationResolverFromMarkers::new(BTreeMap::from([( + Epoch(0), + ProtocolConfigurationForEpoch { + protocol_parameters: protocol_parameters.clone(), + enabled_signed_entity_types: BTreeSet::from([ + SignedEntityTypeDiscriminants::CardanoTransactions, + ]), + cardano_transactions: Some(CardanoTransactionsSigningConfig { + security_parameter: BlockNumberOffset(0), + step: BlockNumber(30), + }), + cardano_blocks_transactions: None, + }, + )])); let mut tester = RuntimeTester::build( TimePoint { epoch: Epoch(1), @@ -44,6 +58,7 @@ async fn prove_transactions() { }, }, configuration, + protocol_configuration_markers, ) .await; let observer = tester.observer.clone(); diff --git a/mithril-aggregator/tests/signed_entity_support.rs b/mithril-aggregator/tests/signed_entity_support.rs index 8f7bf0f5f9c..6a1a3e3eeba 100644 --- a/mithril-aggregator/tests/signed_entity_support.rs +++ b/mithril-aggregator/tests/signed_entity_support.rs @@ -1,5 +1,7 @@ mod test_extensions; +use std::collections::{BTreeMap, BTreeSet}; + use mithril_aggregator::ServeCommandConfiguration; use mithril_common::{ current_function, @@ -10,6 +12,9 @@ use mithril_common::{ temp_dir, test::builder::MithrilFixtureBuilder, }; +use mithril_protocol_config::model::{ + ConfigurationResolverFromMarkers, ProtocolConfigurationForEpoch, +}; use test_extensions::{ExpectedCertificate, RuntimeTester, utilities::get_test_dir}; #[tokio::test] @@ -30,13 +35,27 @@ async fn follower_can_cycle_to_ready_to_signing_with_unknown_and_discontinued_si }, }; let leader_configuration = ServeCommandConfiguration { - protocol_parameters: Some(protocol_parameters.clone()), data_stores_directory: test_dir.join("leader"), signed_entity_types: None, ..ServeCommandConfiguration::new_sample(temp_dir!()) }; - let mut leader_tester = - RuntimeTester::build(start_time_point.clone(), leader_configuration.clone()).await; + let protocol_configuration_markers = ConfigurationResolverFromMarkers::new(BTreeMap::from([( + Epoch(0), + ProtocolConfigurationForEpoch { + protocol_parameters: protocol_parameters.clone(), + enabled_signed_entity_types: BTreeSet::from([ + SignedEntityTypeDiscriminants::MithrilStakeDistribution, + ]), + cardano_transactions: None, + cardano_blocks_transactions: None, + }, + )])); + let mut leader_tester = RuntimeTester::build( + start_time_point.clone(), + leader_configuration.clone(), + protocol_configuration_markers.clone(), + ) + .await; let leader_aggregator_http_server = leader_tester .build_leader_aggregator_http_server() .with_unknown_signed_entities_in_protocol_configuration() @@ -48,12 +67,14 @@ async fn follower_can_cycle_to_ready_to_signing_with_unknown_and_discontinued_si data_stores_directory: test_dir.join("follower"), snapshot_directory: test_dir.join("follower/snapshot"), leader_aggregator_endpoint: Some(leader_aggregator_http_server.url().to_string()), - // Follower must retrieve parameters from the network configuration (today through the leader) - // so this parameters should not be read - protocol_parameters: None, ..leader_configuration }; - let mut follower_tester = RuntimeTester::build(start_time_point, follower_configuration).await; + let mut follower_tester = RuntimeTester::build( + start_time_point, + follower_configuration, + protocol_configuration_markers, + ) + .await; comment!("create signers & declare stake distribution"); let fixture = MithrilFixtureBuilder::default() diff --git a/mithril-aggregator/tests/test_extensions/runtime_tester.rs b/mithril-aggregator/tests/test_extensions/runtime_tester.rs index b053ac3a03d..cdd7d2fb55a 100644 --- a/mithril-aggregator/tests/test_extensions/runtime_tester.rs +++ b/mithril-aggregator/tests/test_extensions/runtime_tester.rs @@ -1,5 +1,8 @@ use anyhow::{Context, anyhow}; use chrono::Utc; +use mithril_protocol_config::{ + model::ConfigurationResolverFromMarkers, test::double::FakeProtocolConfigurationMarkersReader, +}; use slog::Drain; use slog_scope::debug; use std::sync::Arc; @@ -155,6 +158,7 @@ impl RuntimeTester { pub async fn build( start_time_point: TimePoint, configuration: ServeCommandConfiguration, + protocol_configuration_markers: ConfigurationResolverFromMarkers, ) -> Self { let logger = build_logger(); let global_logger = slog_scope::set_global_logger(logger.clone()); @@ -176,6 +180,10 @@ impl RuntimeTester { Some(Epoch(0)), )])); let block_scanner = Arc::new(DumbBlockScanner::new()); + let protocol_configuration_reader = + Arc::new(FakeProtocolConfigurationMarkersReader::default()); + protocol_configuration_reader.set_markers(protocol_configuration_markers); + let mut deps_builder = DependenciesBuilder::new(logger.clone(), Arc::new(configuration)); deps_builder.snapshot_uploader = Some(snapshot_uploader.clone()); deps_builder.chain_observer = Some(chain_observer.clone()); @@ -184,6 +192,7 @@ impl RuntimeTester { deps_builder.snapshotter = Some(snapshotter.clone()); deps_builder.era_reader = Some(Arc::new(EraReader::new(era_reader_adapter.clone()))); deps_builder.block_scanner = Some(block_scanner.clone()); + deps_builder.protocol_configuration_reader = Some(protocol_configuration_reader.clone()); let dependencies = deps_builder.build_serve_dependencies_container().await.unwrap(); let runtime = deps_builder.create_aggregator_runner().await.unwrap(); @@ -211,16 +220,27 @@ impl RuntimeTester { } } - pub async fn rebuild(&mut self, configuration: ServeCommandConfiguration) { + pub async fn rebuild( + &mut self, + configuration: ServeCommandConfiguration, + protocol_configuration_markers: ConfigurationResolverFromMarkers, + ) { // Rebuild only test doubles that were not stored in the RuntimeTester let snapshotter = Arc::new(FakeSnapshotter::new( configuration.get_snapshot_dir().unwrap().join("fake_snapshots"), )); + let protocol_configuration_reader = + Arc::new(FakeProtocolConfigurationMarkersReader::default()); + protocol_configuration_reader.set_markers(protocol_configuration_markers); + let mut deps_builder = DependenciesBuilder::new(slog_scope::logger(), Arc::new(configuration)); deps_builder.snapshotter = Some(snapshotter); + // Re-build protocol configuration reader from input parameter + deps_builder.protocol_configuration_reader = Some(protocol_configuration_reader.clone()); + // Re-use tests doubles deps_builder.snapshot_uploader = Some(self.snapshot_uploader.clone()); deps_builder.chain_observer = Some(self.chain_observer.clone()); diff --git a/mithril-client/Cargo.toml b/mithril-client/Cargo.toml index 9d9105e5807..e326fe8ba20 100644 --- a/mithril-client/Cargo.toml +++ b/mithril-client/Cargo.toml @@ -63,7 +63,7 @@ chrono = { workspace = true } flume = { version = "0.12.0", optional = true } futures = "0.3.32" mithril-aggregator-client = { path = "../internal/mithril-aggregator-client", version = "0.2.4" } -mithril-common = { path = "../mithril-common", version = "0.7.17", default-features = false } +mithril-common = { path = "../mithril-common", version = "0.7.18", default-features = false } reqwest = { workspace = true, default-features = false, features = ["charset", "http2", "stream", "system-proxy"] } serde = { workspace = true } serde_json = { workspace = true } diff --git a/mithril-common/Cargo.toml b/mithril-common/Cargo.toml index b30e55efe10..529db967edb 100644 --- a/mithril-common/Cargo.toml +++ b/mithril-common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mithril-common" -version = "0.7.17" +version = "0.7.18" description = "Common types, interfaces, and utilities for Mithril nodes." authors = { workspace = true } edition = { workspace = true } diff --git a/mithril-common/src/crypto_helper/ed25519_alias.rs b/mithril-common/src/crypto_helper/ed25519_alias.rs index 9d3694a3258..6dcbcc9e98a 100644 --- a/mithril-common/src/crypto_helper/ed25519_alias.rs +++ b/mithril-common/src/crypto_helper/ed25519_alias.rs @@ -15,7 +15,10 @@ pub mod era { pub type EraMarkersVerifier = Ed25519Verifier; /// [EraMarkersSigner] and [EraMarkersVerifier] related errors. pub type EraMarkersVerifierError = Ed25519VerifierError; +} +pub mod protocol_configuration { + use super::*; /// Protocol Configuration markers verifier secret key pub type ProtocolConfigurationMarkersVerifierSecretKey = Ed25519SecretKey; /// Protocol Configuration markers verifier verification key @@ -24,6 +27,8 @@ pub mod era { pub type ProtocolConfigurationMarkersVerifierSignature = Ed25519Signature; /// A cryptographic signer that is responsible for signing the ProtocolConfigurationMarkers pub type ProtocolConfigurationMarkersSigner = Ed25519Signer; + /// A protocol configuration markers verifier that checks the authenticity of protocol configuration markers stored on the chain + pub type ProtocolConfigurationMarkersVerifier = Ed25519Verifier; } pub mod manifest { diff --git a/mithril-common/src/crypto_helper/mod.rs b/mithril-common/src/crypto_helper/mod.rs index 6e34741385c..a2937d2804f 100644 --- a/mithril-common/src/crypto_helper/mod.rs +++ b/mithril-common/src/crypto_helper/mod.rs @@ -19,7 +19,7 @@ pub use cardano::{ SignerRegistrationParameters, Sum6KesBytes, }; pub use codec::*; -pub use ed25519_alias::{era::*, manifest::*}; +pub use ed25519_alias::{era::*, manifest::*, protocol_configuration::*}; pub use genesis::*; pub use mithril_merkle_tree::{ Bytes, MKMap, MKMapKey, MKMapNode, MKMapProof, MKMapValue, MKProof, MKTree, MKTreeLeafIndexer, diff --git a/mithril-signer/Cargo.toml b/mithril-signer/Cargo.toml index 8164ac45e37..cabab46b9c3 100644 --- a/mithril-signer/Cargo.toml +++ b/mithril-signer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mithril-signer" -version = "1.1.8" +version = "1.2.0" description = "A Mithril Signer" authors = { workspace = true } edition = { workspace = true } diff --git a/mithril-signer/src/configuration.rs b/mithril-signer/src/configuration.rs index 4e21af25e57..d9ca36924c9 100644 --- a/mithril-signer/src/configuration.rs +++ b/mithril-signer/src/configuration.rs @@ -1,20 +1,22 @@ use anyhow::Context; use config::{ConfigError, Map, Source, Value}; -use mithril_dmq::DmqNetwork; -use mithril_doc::{Documenter, DocumenterDefault, StructDoc}; use serde::Deserialize; use std::{path::PathBuf, sync::Arc}; use mithril_cardano_node_chain::chain_observer::ChainObserver; -use mithril_cli_helper::register_config_value; +use mithril_cli_helper::{register_config_value, serde_deserialization}; use mithril_common::{ CardanoNetwork, StdResult, + crypto_helper::ProtocolConfigurationMarkersSigner, entities::{BlockNumber, PartyId}, }; +use mithril_dmq::DmqNetwork; +use mithril_doc::{Documenter, DocumenterDefault, StructDoc}; use mithril_era::{ EraReaderAdapter, adapters::{EraReaderAdapterBuilder, EraReaderAdapterType}, }; +use mithril_protocol_config::builder::AdapterConfig; #[derive(Debug, Clone, Deserialize)] pub struct SignaturePublisherConfig { @@ -117,6 +119,16 @@ pub struct Configuration { /// Era reader adapter parameters pub era_reader_adapter_params: Option, + /// Protocol configuration reader adapter configuration + #[example = "\ + - cardano-chain:
`{ \"type\": \"cardano-chain\", \"address\": \"test_address\", \"verification_key\": \"136372c3138312c3138382c3130352c3233312c3135\" }`
\ + "] + #[serde( + default, + deserialize_with = "serde_deserialization::string_or_struct_optional" + )] + pub protocol_configuration_reader_adapter_config: Option, + /// Enable metrics server (Prometheus endpoint on /metrics). pub enable_metrics_server: bool, @@ -188,6 +200,13 @@ impl Configuration { reset_digests_cache: false, era_reader_adapter_type: EraReaderAdapterType::Bootstrap, era_reader_adapter_params: None, + protocol_configuration_reader_adapter_config: Some(AdapterConfig::CardanoChain { + address: "address".to_string(), + verification_key: Box::new( + ProtocolConfigurationMarkersSigner::create_deterministic_signer() + .verification_key(), + ), + }), enable_metrics_server: true, metrics_server_ip: "0.0.0.0".to_string(), metrics_server_port: 9090, diff --git a/mithril-signer/src/dependency_injection/builder.rs b/mithril-signer/src/dependency_injection/builder.rs index 8e3ce09955c..29bbb457d3d 100644 --- a/mithril-signer/src/dependency_injection/builder.rs +++ b/mithril-signer/src/dependency_injection/builder.rs @@ -46,7 +46,11 @@ use mithril_persistence::sqlite::{ ConnectionBuilder, ConnectionOptions, SqliteConnection, SqliteConnectionPool, }; -use mithril_protocol_config::http::HttpMithrilNetworkConfigurationProvider; +use mithril_protocol_config::{ + builder::build_protocol_configuration_adapter, http::HttpMithrilNetworkConfigurationProvider, + interface::MithrilNetworkConfigurationProvider, + markers::MarkersMithrilNetworkConfigurationProvider, +}; use mithril_dmq::{DmqMessageBuilder, DmqPublisherClientPallas}; @@ -187,6 +191,27 @@ impl<'a> DependenciesBuilder<'a> { Ok(Some(Arc::new(cache_provider))) } + /// Build a MithrilNetworkConfigurationProvider + pub async fn build_mithril_network_configuration_provider( + &self, + chain_observer: Arc, + aggregator_client: Arc, + ) -> Arc { + if let Some(config) = self.config.protocol_configuration_reader_adapter_config.clone() { + let protocol_configuration_adapter = + build_protocol_configuration_adapter(config, chain_observer); + Arc::new(MarkersMithrilNetworkConfigurationProvider::new( + self.root_logger(), + protocol_configuration_adapter, + )) + } else { + Arc::new(HttpMithrilNetworkConfigurationProvider::new( + aggregator_client, + self.root_logger(), + )) + } + } + /// Build an SQLite connection for the main database. pub async fn build_main_sqlite_connection( &self, @@ -420,10 +445,14 @@ impl<'a> DependenciesBuilder<'a> { self.root_logger(), )); let metrics_service = Arc::new(MetricsService::new(self.root_logger())?); - let network_configuration_service = Arc::new(HttpMithrilNetworkConfigurationProvider::new( - aggregator_client.clone(), - self.root_logger(), - )); + + let network_configuration_service = self + .build_mithril_network_configuration_provider( + chain_observer.clone(), + aggregator_client.clone(), + ) + .await; + let preloader_activation = CardanoTransactionsPreloaderActivationSigner::new( network_configuration_service.clone(), ticker_service.clone(), diff --git a/mithril-test-lab/cardano-devnet/VERSION b/mithril-test-lab/cardano-devnet/VERSION index 467efdd39eb..ff0e310dbc4 100644 --- a/mithril-test-lab/cardano-devnet/VERSION +++ b/mithril-test-lab/cardano-devnet/VERSION @@ -1,2 +1,2 @@ -0.4.40 +0.4.41 diff --git a/mithril-test-lab/cardano-devnet/devnet-mkfiles.sh b/mithril-test-lab/cardano-devnet/devnet-mkfiles.sh index 3878e78ed20..e43903e5b19 100755 --- a/mithril-test-lab/cardano-devnet/devnet-mkfiles.sh +++ b/mithril-test-lab/cardano-devnet/devnet-mkfiles.sh @@ -34,6 +34,9 @@ SCRIPT_DIRECTORY=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P) # Generate the Mithril delegation scripts . ${SCRIPT_DIRECTORY}/mkfiles/mkfiles-mithril-delegation.sh +# Generate the Cardano chain lib file +. ${SCRIPT_DIRECTORY}/mkfiles/lib/mkfiles-cardano-chain.sh + # Generate the Mithril era scripts . ${SCRIPT_DIRECTORY}/mkfiles/mkfiles-mithril-era.sh diff --git a/mithril-test-lab/cardano-devnet/mkfiles/lib/mkfiles-cardano-chain.sh b/mithril-test-lab/cardano-devnet/mkfiles/lib/mkfiles-cardano-chain.sh new file mode 100644 index 00000000000..3963438d362 --- /dev/null +++ b/mithril-test-lab/cardano-devnet/mkfiles/lib/mkfiles-cardano-chain.sh @@ -0,0 +1,242 @@ +cat >> cardano-chain.sh <>>> Cardano target block not reached yet... [current: \$CARDANO_BLOCK, target: \$CARDANO_BLOCK_TARGET] [attempt \$CARDANO_NEXT_BLOCK_WAIT_ROUNDS]" + sleep \$CARDANO_NEXT_BLOCK_WAIT_ROUND_DELAY + else + echo ">>>> Cardano target block is reached [current: \$CARDANO_BLOCK, target: \$CARDANO_BLOCK_TARGET] [attempt \$CARDANO_NEXT_BLOCK_WAIT_ROUNDS]" + break + fi + CARDANO_NEXT_BLOCK_WAIT_ROUNDS=\$(( \$CARDANO_NEXT_BLOCK_WAIT_ROUNDS + 1 )) + if [ "\$CARDANO_NEXT_BLOCK_WAIT_ROUNDS" -gt "\$CARDANO_NEXT_BLOCK_WAIT_ROUNDS_MAX" ] ; then + echo ">>>> Timeout: Cardano target block was not reached within \$CARDANO_NEXT_BLOCK_WAIT_ROUNDS_MAX attempts" + exit 1 + fi + done +} + +# Send funds to address +function send_funds_to_address { + ADDR=\$1 + AMOUNT_TRANSFERRED=\$2 + CONTEXT=\$3 + N=\$4 + + echo ">>>> Gonna send_funds_to_address \${ADDR} \${AMOUNT_TRANSFERRED} \${CONTEXT} \${N}" + + # Remove if exists previous error file + ERROR_FILE=\${CONTEXT}-error + rm -f -- "\${ERROR_FILE}" + + # Get current Cardano era + CURRENT_CARDANO_ERA=\$(CARDANO_NODE_SOCKET_PATH=node-pool\${N}/ipc/node.sock $CARDANO_CLI $CARDANO_CLI_ERA query tip \\ + --testnet-magic ${NETWORK_MAGIC} \\ + | jq -r '.era |= ascii_downcase | .era') + echo ">>>> Current Cardano Era: \${CURRENT_CARDANO_ERA}" + + # Get current Cardano block + CURRENT_CARDANO_BLOCK=\$(CARDANO_NODE_SOCKET_PATH=node-pool\${N}/ipc/node.sock $CARDANO_CLI $CARDANO_CLI_ERA query tip \\ + --testnet-magic ${NETWORK_MAGIC} \\ + | jq -r '.block') + echo ">>>> Current Cardano Block: \${CURRENT_CARDANO_BLOCK}" + + # Send funds to address + ## Get the UTxO of utxo\${N} + TX_IN=\$(CARDANO_NODE_SOCKET_PATH=node-pool\${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} query utxo \\ + --testnet-magic ${NETWORK_MAGIC} --address \$(cat addresses/utxo\${N}.addr) --out-file /dev/stdout \\ + | jq -r 'to_entries | [last] | .[0].key') + + ## Build the transaction + CARDANO_NODE_SOCKET_PATH=node-pool\${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction build \\ + --tx-in \${TX_IN} \\ + --tx-out \$(cat addresses/\${ADDR}.addr)+\${AMOUNT_TRANSFERRED} \\ + --change-address \$(cat addresses/utxo\${N}.addr) \\ + --testnet-magic ${NETWORK_MAGIC} \\ + --invalid-hereafter 100000 \\ + --out-file node-pool\${N}/tx/tx\${N}-\${CONTEXT}-funds.txbody + + ## Sign the transaction + CARDANO_NODE_SOCKET_PATH=node-pool\${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction sign \\ + --signing-key-file addresses/utxo\${N}.skey \\ + --testnet-magic ${NETWORK_MAGIC} \\ + --tx-body-file node-pool\${N}/tx/tx\${N}-\${CONTEXT}-funds.txbody \\ + --out-file node-pool\${N}/tx/tx\${N}-\${CONTEXT}-funds.tx + + ## Submit the transaction + CARDANO_NODE_SOCKET_PATH=node-pool\${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction submit \\ + --tx-file node-pool\${N}/tx/tx\${N}-\${CONTEXT}-funds.tx \\ + --testnet-magic ${NETWORK_MAGIC} + + ## Compute the submitted transaction id + TX_ID_SUBMITTED=\$(CARDANO_NODE_SOCKET_PATH=node-pool\${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction txid --tx-file node-pool\${N}/tx/tx\${N}-\${CONTEXT}-funds.tx) + if [[ "\${TX_ID_SUBMITTED}" =~ txhash ]]; then + TX_ID_SUBMITTED=\$(echo \$TX_ID_SUBMITTED | jq -r '.txhash') + fi + + ## Wait at least for 10 blocks so that the transaction is confirmed + wait_for_elapsed_blocks "\${N}" 10 + + ## Wait for all pool nodes to see the new transaction + for (( i=1; i<=${NUM_POOL_NODES}; i++ )); do + TOTAL_UTXOS_FOR_TX_ID=\$(CARDANO_NODE_SOCKET_PATH=node-pool\${i}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} query utxo \\ + --testnet-magic ${NETWORK_MAGIC} --address \$(cat addresses/\${ADDR}.addr) --out-file /dev/stdout \\ + | jq '. | with_entries(select(.key | startswith("${TX_ID_SUBMITTED}"))) | length') + echo ">>>>>> \${CONTEXT} address funds retrieved on node-pool\${i}: \${TOTAL_UTXOS_FOR_TX_ID}" + if [ "\${TOTAL_UTXOS_FOR_TX_ID}" == "0" ]; then + touch "\${ERROR_FILE}" + break + fi + done +} + +# Try to send funds address +function try_send_funds_to_address { + ADDR=\$1 + AMOUNT_TRANSFERRED=\$2 + CONTEXT=\$3 + N=\$4 + + ERROR_FILE=\${CONTEXT}-error + SEND_FUNDS_ADDRESS_WAIT_ROUNDS_MAX=10 + SEND_FUNDS_ADDRESS_WAIT_ROUNDS=1 + SEND_FUNDS_ADDRESS_WAIT_ROUND_DELAY=2 + while true + do + send_funds_to_address "\${ADDR}" "\${AMOUNT_TRANSFERRED}" "\${CONTEXT}" "\${N}" + if [ -f \${ERROR_FILE} ]; then + echo ">>>> Funds not transferred successfully to \${CONTEXT} address, a rollback has happened [attempt \$SEND_FUNDS_ADDRESS_WAIT_ROUNDS]!" + sleep \$SEND_FUNDS_ADDRESS_WAIT_ROUND_DELAY + else + echo ">>>> Funds transferred successfully to \${CONTEXT} address [attempt \$SEND_FUNDS_ADDRESS_WAIT_ROUNDS]!" + break + fi + SEND_FUNDS_ADDRESS_WAIT_ROUNDS=\$(( \$SEND_FUNDS_ADDRESS_WAIT_ROUNDS + 1 )) + if [ "\$SEND_FUNDS_ADDRESS_WAIT_ROUNDS" -gt "\$SEND_FUNDS_ADDRESS_WAIT_ROUNDS_MAX" ] ; then + echo ">>>> Timeout: Funds were not transferred successfully to \${CONTEXT} address within \$SEND_FUNDS_ADDRESS_WAIT_ROUNDS_MAX attempts" + exit 1 + fi + done +} + +# Write datums for address +function write_datums_for_address { + ADDR=\$1 + SCRIPT_TX_VALUE=\$2 + CONTEXT=\$3 + N=\$4 + DATUM_FILE=\$5 + + # Remove if exists previous error file + ERROR_FILE=\${CONTEXT}-error + rm -f -- "\${ERROR_FILE}" + + # Fetch transactions from UTxOs of the address + TX_IN_DATUM=\$(CARDANO_NODE_SOCKET_PATH=node-pool\${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} query utxo \\ + --testnet-magic ${NETWORK_MAGIC} --address \$(cat addresses/\${ADDR}.addr) --out-file /dev/stdout \\ + | jq -r 'to_entries | map({utxo: .key} + .value) | . [] | select(.inlineDatum | . != null and . != "") | .utxo') + TX_IN_NO_DATUM=\$(CARDANO_NODE_SOCKET_PATH=node-pool\${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} query utxo \\ + --testnet-magic ${NETWORK_MAGIC} --address \$(cat addresses/\${ADDR}.addr) --out-file /dev/stdout \\ + | jq -r 'to_entries | [last] | .[0].key') + + ## Build the transaction + if [ "\${TX_IN_DATUM}" == "" ]; then + CARDANO_NODE_SOCKET_PATH=node-pool\${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction build \\ + --tx-in \${TX_IN_NO_DATUM} \\ + --tx-out \$(cat addresses/\${ADDR}.addr)+\${SCRIPT_TX_VALUE} \\ + --tx-out-inline-datum-file \${DATUM_FILE} \\ + --change-address \$(cat addresses/\${ADDR}.addr) \\ + --testnet-magic ${NETWORK_MAGIC} \\ + --invalid-hereafter 100000 \\ + --out-file node-pool\${N}/tx/tx\${N}-\${CONTEXT}-datum.txbody + else + CARDANO_NODE_SOCKET_PATH=node-pool\${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction build \\ + --tx-in \${TX_IN_DATUM} \\ + --tx-in \${TX_IN_NO_DATUM} \\ + --tx-out \$(cat addresses/\${ADDR}.addr)+\${SCRIPT_TX_VALUE} \\ + --tx-out-inline-datum-file \${DATUM_FILE} \\ + --change-address \$(cat addresses/\${ADDR}.addr) \\ + --testnet-magic ${NETWORK_MAGIC} \\ + --invalid-hereafter 100000 \\ + --out-file node-pool\${N}/tx/tx\${N}-\${CONTEXT}-datum.txbody + fi + + ## Sign the transaction + CARDANO_NODE_SOCKET_PATH=node-pool\${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction sign \\ + --signing-key-file addresses/\${ADDR}.skey \\ + --testnet-magic ${NETWORK_MAGIC} \\ + --tx-body-file node-pool\${N}/tx/tx\${N}-\${CONTEXT}-datum.txbody \\ + --out-file node-pool\${N}/tx/tx\${N}-\${CONTEXT}-datum.tx + + ## Submit the transaction + CARDANO_NODE_SOCKET_PATH=node-pool\${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction submit \\ + --tx-file node-pool\${N}/tx/tx\${N}-\${CONTEXT}-datum.tx \\ + --testnet-magic ${NETWORK_MAGIC} + + ## Wait at least for 10 blocks so that the transaction is confirmed + wait_for_elapsed_blocks "\${N}" 10 + + ## Wait for all pool nodes to see the new datum + for (( i=1; i<=${NUM_POOL_NODES}; i++ )); do + INLINE_DATUM=\$(CARDANO_NODE_SOCKET_PATH=node-pool\${i}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} query utxo \\ + --testnet-magic ${NETWORK_MAGIC} --address \$(cat addresses/\${ADDR}.addr) --out-file /dev/stdout \\ + | jq -r '. [] | select(.inlineDatum | . != null and . != "") | .inlineDatum.fields[].bytes' | xxd -r -p | jq) + echo ">>>>>> \${CONTEXT} address inline datum retrieved on node-pool\${i}: \${INLINE_DATUM}" + if [ "\${INLINE_DATUM}" == "" ]; then + touch \${ERROR_FILE} + break + fi + done +} + +# Try to write datums address +function try_write_datums_for_address { + ADDR=\$1 + SCRIPT_TX_VALUE=\$2 + CONTEXT=\$3 + N=\$4 + DATUM_FILE=\$5 + + ERROR_FILE=\${CONTEXT}-error + WRITE_DATUMS_ADDRESS_WAIT_ROUNDS_MAX=10 + WRITE_DATUMS_ADDRESS_WAIT_ROUNDS=1 + WRITE_DATUMS_ADDRESS_WAIT_ROUND_DELAY=2 + while true + do + write_datums_for_address "\${ADDR}" "\${SCRIPT_TX_VALUE}" \${CONTEXT} "\${N}" "\${DATUM_FILE}" + if [ -f \${ERROR_FILE} ]; then + echo ">>>> Datums not written successfully for \${CONTEXT} address, a rollback has happened [attempt \$WRITE_DATUMS_ADDRESS_WAIT_ROUNDS]!" + sleep \$WRITE_DATUMS_ADDRESS_WAIT_ROUND_DELAY + else + echo ">>>> Datums successfully written for \${CONTEXT} address [attempt \$WRITE_DATUMS_ADDRESS_WAIT_ROUNDS]!" + break + fi + WRITE_DATUMS_ADDRESS_WAIT_ROUNDS=\$(( \$WRITE_DATUMS_ADDRESS_WAIT_ROUNDS + 1 )) + if [ "\$WRITE_DATUMS_ADDRESS_WAIT_ROUNDS" -gt "\$WRITE_DATUMS_ADDRESS_WAIT_ROUNDS_MAX" ] ; then + echo ">>>> Timeout: Datums were not written successfully for \${CONTEXT} address within \$WRITE_DATUMS_ADDRESS_WAIT_ROUNDS_MAX attempts" + exit 1 + fi + done +} + +EOF + +chmod u+x cardano-chain.sh \ No newline at end of file diff --git a/mithril-test-lab/cardano-devnet/mkfiles/mkfiles-mithril-era.sh b/mithril-test-lab/cardano-devnet/mkfiles/mkfiles-mithril-era.sh index eea7c2e6be7..ec3747395c3 100644 --- a/mithril-test-lab/cardano-devnet/mkfiles/mkfiles-mithril-era.sh +++ b/mithril-test-lab/cardano-devnet/mkfiles/mkfiles-mithril-era.sh @@ -2,235 +2,32 @@ ADDR=mithril-era ## Payment address keys -$CARDANO_CLI $CARDANO_CLI_ERA address key-gen \ +$CARDANO_CLI "$CARDANO_CLI_ERA" address key-gen \ --verification-key-file addresses/${ADDR}.vkey \ --signing-key-file addresses/${ADDR}.skey ## Payment addresses -$CARDANO_CLI $CARDANO_CLI_ERA address build \ +$CARDANO_CLI "$CARDANO_CLI_ERA" address build \ --payment-verification-key-file addresses/${ADDR}.vkey \ - --testnet-magic ${NETWORK_MAGIC} \ + --testnet-magic "${NETWORK_MAGIC}" \ --out-file addresses/${ADDR}.addr ## Write datums for Mithril era address N=1 SCRIPT_TX_VALUE=2000000 AMOUNT_TRANSFERRED=$(( SCRIPT_TX_VALUE * 10 )) -MITHRIL_ERA_ERROR_FILE=./era-mithril-error + cat >> era-mithril.sh <>>> Cardano target block not reached yet... [current: \$CARDANO_BLOCK, target: \$CARDANO_BLOCK_TARGET] [attempt \$CARDANO_NEXT_BLOCK_WAIT_ROUNDS]" - sleep \$CARDANO_NEXT_BLOCK_WAIT_ROUND_DELAY - else - echo ">>>> Cardano target block is reached [current: \$CARDANO_BLOCK, target: \$CARDANO_BLOCK_TARGET] [attempt \$CARDANO_NEXT_BLOCK_WAIT_ROUNDS]" - break - fi - CARDANO_NEXT_BLOCK_WAIT_ROUNDS=\$(( \$CARDANO_NEXT_BLOCK_WAIT_ROUNDS + 1 )) - if [ "\$CARDANO_NEXT_BLOCK_WAIT_ROUNDS" -gt "\$CARDANO_NEXT_BLOCK_WAIT_ROUNDS_MAX" ] ; then - echo ">>>> Timeout: Cardano target block was not reached within \$CARDANO_NEXT_BLOCK_WAIT_ROUNDS_MAX attempts" - exit 1 - fi - done -} - -# Send funds to Mithril era address -function send_funds_to_era_address { - # Remove if exists previous error file - rm -f ${MITHRIL_ERA_ERROR_FILE} - - # Get current Cardano era - CURRENT_CARDANO_ERA=\$(CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI $CARDANO_CLI_ERA query tip \\ - --testnet-magic ${NETWORK_MAGIC} \\ - | jq -r '.era |= ascii_downcase | .era') - echo ">>>> Current Cardano Era: \${CURRENT_CARDANO_ERA}" - - # Get current Cardano block - CURRENT_CARDANO_BLOCK=\$(CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI $CARDANO_CLI_ERA query tip \\ - --testnet-magic ${NETWORK_MAGIC} \\ - | jq -r '.block') - echo ">>>> Current Cardano Block: \${CURRENT_CARDANO_BLOCK}" - - # Send funds to Mithril era address - ## Get the UTxO of utxo${N} - TX_IN=\$(CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} query utxo \\ - --testnet-magic ${NETWORK_MAGIC} --address \$(cat addresses/utxo${N}.addr) --out-file /dev/stdout \\ - | jq -r 'to_entries | [last] | .[0].key') - - ## Build the transaction - CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction build \\ - --tx-in \${TX_IN} \\ - --tx-out \$(cat addresses/${ADDR}.addr)+${AMOUNT_TRANSFERRED} \\ - --change-address \$(cat addresses/utxo${N}.addr) \\ - --testnet-magic ${NETWORK_MAGIC} \\ - --invalid-hereafter 100000 \\ - --out-file node-pool${N}/tx/tx${N}-era-funds.txbody - - ## Sign the transaction - CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction sign \\ - --signing-key-file addresses/utxo${N}.skey \\ - --testnet-magic ${NETWORK_MAGIC} \\ - --tx-body-file node-pool${N}/tx/tx${N}-era-funds.txbody \\ - --out-file node-pool${N}/tx/tx${N}-era-funds.tx - - ## Submit the transaction - CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction submit \\ - --tx-file node-pool${N}/tx/tx${N}-era-funds.tx \\ - --testnet-magic ${NETWORK_MAGIC} - - ## Compute the submitted transaction id - TX_ID_SUBMITTED=\$(CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction txid --tx-file node-pool${N}/tx/tx${N}-era-funds.tx) - if [[ "\${TX_ID_SUBMITTED}" =~ txhash ]]; then - TX_ID_SUBMITTED=\$(echo \$TX_ID_SUBMITTED | jq -r '.txhash') - fi - - ## Wait at least for 10 blocks so that the transaction is confirmed - wait_for_elapsed_blocks 10 - - ## Wait for all pool nodes to see the new transaction - for (( i=1; i<=${NUM_POOL_NODES}; i++ )); do - TOTAL_UTXOS_FOR_TX_ID=\$(CARDANO_NODE_SOCKET_PATH=node-pool\${i}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} query utxo \\ - --testnet-magic ${NETWORK_MAGIC} --address \$(cat addresses/${ADDR}.addr) --out-file /dev/stdout \\ - | jq '. | with_entries(select(.key | startswith("${TX_ID_SUBMITTED}"))) | length') - echo ">>>>>> Era address funds retrieved on node-pool\${i}: \${AMOUNT_RETRIEVED}" - if [ "\${TOTAL_UTXOS_FOR_TX_ID}" == "0" ]; then - touch ${MITHRIL_ERA_ERROR_FILE} - break - fi - done -} - -# Try to send funds to Mithril era address -function try_send_funds_to_era_address { - SEND_FUNDS_ERA_ADDRESS_WAIT_ROUNDS_MAX=10 - SEND_FUNDS_ERA_ADDRESS_WAIT_ROUNDS=1 - SEND_FUNDS_ERA_ADDRESS_WAIT_ROUND_DELAY=2 - while true - do - send_funds_to_era_address - if [ -f ${MITHRIL_ERA_ERROR_FILE} ]; then - echo ">>>> Funds not transferred successfully to Mithril era address, a rollback has happened [attempt \$SEND_FUNDS_ERA_ADDRESS_WAIT_ROUNDS]!" - sleep \$SEND_FUNDS_ERA_ADDRESS_WAIT_ROUND_DELAY - else - echo ">>>> Funds transferred successfully to Mithril era address [attempt \$SEND_FUNDS_ERA_ADDRESS_WAIT_ROUNDS]!" - break - fi - SEND_FUNDS_ERA_ADDRESS_WAIT_ROUNDS=\$(( \$SEND_FUNDS_ERA_ADDRESS_WAIT_ROUNDS + 1 )) - if [ "\$SEND_FUNDS_ERA_ADDRESS_WAIT_ROUNDS" -gt "\$SEND_FUNDS_ERA_ADDRESS_WAIT_ROUNDS_MAX" ] ; then - echo ">>>> Timeout: Funds were not transferred successfully to Mithril era address within \$SEND_FUNDS_ERA_ADDRESS_WAIT_ROUNDS_MAX attempts" - exit 1 - fi - done -} - -# Write datums for Mithril era address -function write_datums_for_era_address { - # Remove if exists previous error file - rm -f ${MITHRIL_ERA_ERROR_FILE} - - # Fetch transactions from UTxOs of the era address - TX_IN_DATUM=\$(CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} query utxo \\ - --testnet-magic ${NETWORK_MAGIC} --address \$(cat addresses/${ADDR}.addr) --out-file /dev/stdout \\ - | jq -r 'to_entries | map({utxo: .key} + .value) | . [] | select(.inlineDatum | . != null and . != "") | .utxo') - TX_IN_NO_DATUM=\$(CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} query utxo \\ - --testnet-magic ${NETWORK_MAGIC} --address \$(cat addresses/${ADDR}.addr) --out-file /dev/stdout \\ - | jq -r 'to_entries | [last] | .[0].key') - - ## Build the transaction - if [ "\${TX_IN_DATUM}" == "" ]; then - CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction build \\ - --tx-in \${TX_IN_NO_DATUM} \\ - --tx-out \$(cat addresses/${ADDR}.addr)+${SCRIPT_TX_VALUE} \\ - --tx-out-inline-datum-file \${DATUM_FILE} \\ - --change-address \$(cat addresses/${ADDR}.addr) \\ - --testnet-magic ${NETWORK_MAGIC} \\ - --invalid-hereafter 100000 \\ - --out-file node-pool${N}/tx/tx${N}-era-datum.txbody - else - CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction build \\ - --tx-in \${TX_IN_DATUM} \\ - --tx-in \${TX_IN_NO_DATUM} \\ - --tx-out \$(cat addresses/${ADDR}.addr)+${SCRIPT_TX_VALUE} \\ - --tx-out-inline-datum-file \${DATUM_FILE} \\ - --change-address \$(cat addresses/${ADDR}.addr) \\ - --testnet-magic ${NETWORK_MAGIC} \\ - --invalid-hereafter 100000 \\ - --out-file node-pool${N}/tx/tx${N}-era-datum.txbody - fi - - ## Sign the transaction - CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction sign \\ - --signing-key-file addresses/${ADDR}.skey \\ - --testnet-magic ${NETWORK_MAGIC} \\ - --tx-body-file node-pool${N}/tx/tx${N}-era-datum.txbody \\ - --out-file node-pool${N}/tx/tx${N}-era-datum.tx - - ## Submit the transaction - CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction submit \\ - --tx-file node-pool${N}/tx/tx${N}-era-datum.tx \\ - --testnet-magic ${NETWORK_MAGIC} - - ## Wait at least for 10 blocks so that the transaction is confirmed - wait_for_elapsed_blocks 10 - - ## Wait for all pool nodes to see the new era datum - for (( i=1; i<=${NUM_POOL_NODES}; i++ )); do - INLINE_DATUM=\$(CARDANO_NODE_SOCKET_PATH=node-pool\${i}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} query utxo \\ - --testnet-magic ${NETWORK_MAGIC} --address \$(cat addresses/${ADDR}.addr) --out-file /dev/stdout \\ - | jq -r '. [] | select(.inlineDatum | . != null and . != "") | .inlineDatum.fields[].bytes' | xxd -r -p | jq) - echo ">>>>>> Era address inline datum retrieved on node-pool\${i}: \${INLINE_DATUM}" - if [ "\${INLINE_DATUM}" == "" ]; then - touch ${MITHRIL_ERA_ERROR_FILE} - break - fi - done -} - -# Try to write datums for Mithril era address -function try_write_datums_for_era_address { - WRITE_DATUMS_ERA_ADDRESS_WAIT_ROUNDS_MAX=10 - WRITE_DATUMS_ERA_ADDRESS_WAIT_ROUNDS=1 - WRITE_DATUMS_ERA_ADDRESS_WAIT_ROUND_DELAY=2 - while true - do - write_datums_for_era_address - if [ -f ${MITHRIL_ERA_ERROR_FILE} ]; then - echo ">>>> Datums not written successfully for Mithril era address, a rollback has happened [attempt \$WRITE_DATUMS_ERA_ADDRESS_WAIT_ROUNDS]!" - sleep \$WRITE_DATUMS_ERA_ADDRESS_WAIT_ROUND_DELAY - else - echo ">>>> Datums successfully written for Mithril era address [attempt \$WRITE_DATUMS_ERA_ADDRESS_WAIT_ROUNDS]!" - break - fi - WRITE_DATUMS_ERA_ADDRESS_WAIT_ROUNDS=\$(( \$WRITE_DATUMS_ERA_ADDRESS_WAIT_ROUNDS + 1 )) - if [ "\$WRITE_DATUMS_ERA_ADDRESS_WAIT_ROUNDS" -gt "\$WRITE_DATUMS_ERA_ADDRESS_WAIT_ROUNDS_MAX" ] ; then - echo ">>>> Timeout: Datums were not written successfully for Mithril era address within \$WRITE_DATUMS_ERA_ADDRESS_WAIT_ROUNDS_MAX attempts" - exit 1 - fi - done -} +. "cardano-chain.sh" -# Try to send funds to Mithril era address -try_send_funds_to_era_address +# Try to send funds to protocol configuration address +try_send_funds_to_address "${ADDR}" "${AMOUNT_TRANSFERRED}" "era" "${N}" -# Try to write datums for Mithril era address -try_write_datums_for_era_address +# Try to write datums for protocol configuration address +try_write_datums_for_address "${ADDR}" "${SCRIPT_TX_VALUE}" "era" "${N}" "\${DATUM_FILE}" EOF diff --git a/mithril-test-lab/cardano-devnet/mkfiles/mkfiles-protocol-configuration.sh b/mithril-test-lab/cardano-devnet/mkfiles/mkfiles-protocol-configuration.sh index 4132e36b3ad..086e9aafa74 100644 --- a/mithril-test-lab/cardano-devnet/mkfiles/mkfiles-protocol-configuration.sh +++ b/mithril-test-lab/cardano-devnet/mkfiles/mkfiles-protocol-configuration.sh @@ -16,221 +16,18 @@ $CARDANO_CLI "$CARDANO_CLI_ERA" address build \ N=1 SCRIPT_TX_VALUE=7200000 AMOUNT_TRANSFERRED=$(( SCRIPT_TX_VALUE * 10 )) -MITHRIL_PROTOCOL_CONFIG_ERROR_FILE=./protocol-config-error + cat >> protocol-configuration.sh <>>> Cardano target block not reached yet... [current: \$CARDANO_BLOCK, target: \$CARDANO_BLOCK_TARGET] [attempt \$CARDANO_NEXT_BLOCK_WAIT_ROUNDS]" - sleep \$CARDANO_NEXT_BLOCK_WAIT_ROUND_DELAY - else - echo ">>>> Cardano target block is reached [current: \$CARDANO_BLOCK, target: \$CARDANO_BLOCK_TARGET] [attempt \$CARDANO_NEXT_BLOCK_WAIT_ROUNDS]" - break - fi - CARDANO_NEXT_BLOCK_WAIT_ROUNDS=\$(( \$CARDANO_NEXT_BLOCK_WAIT_ROUNDS + 1 )) - if [ "\$CARDANO_NEXT_BLOCK_WAIT_ROUNDS" -gt "\$CARDANO_NEXT_BLOCK_WAIT_ROUNDS_MAX" ] ; then - echo ">>>> Timeout: Cardano target block was not reached within \$CARDANO_NEXT_BLOCK_WAIT_ROUNDS_MAX attempts" - exit 1 - fi - done -} - -# Send funds to protocol configuration address -function send_funds_to_protocol_config_address { - # Remove if exists previous error file - rm -f ${MITHRIL_PROTOCOL_CONFIG_ERROR_FILE} - - # Get current Cardano era - CURRENT_CARDANO_ERA=\$(CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI $CARDANO_CLI_ERA query tip \\ - --testnet-magic ${NETWORK_MAGIC} \\ - | jq -r '.era |= ascii_downcase | .era') - echo ">>>> Current Cardano Era: \${CURRENT_CARDANO_ERA}" - - # Get current Cardano block - CURRENT_CARDANO_BLOCK=\$(CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI $CARDANO_CLI_ERA query tip \\ - --testnet-magic ${NETWORK_MAGIC} \\ - | jq -r '.block') - echo ">>>> Current Cardano Block: \${CURRENT_CARDANO_BLOCK}" - - # Send funds to protocol configuration address - ## Get the UTxO of utxo${N} - TX_IN=\$(CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} query utxo \\ - --testnet-magic ${NETWORK_MAGIC} --address \$(cat addresses/utxo${N}.addr) --out-file /dev/stdout \\ - | jq -r 'to_entries | [last] | .[0].key') - - ## Build the transaction - CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction build \\ - --tx-in \${TX_IN} \\ - --tx-out \$(cat addresses/${ADDR}.addr)+${AMOUNT_TRANSFERRED} \\ - --change-address \$(cat addresses/utxo${N}.addr) \\ - --testnet-magic ${NETWORK_MAGIC} \\ - --invalid-hereafter 100000 \\ - --out-file node-pool${N}/tx/tx${N}-protocol-config-funds.txbody - - ## Sign the transaction - CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction sign \\ - --signing-key-file addresses/utxo${N}.skey \\ - --testnet-magic ${NETWORK_MAGIC} \\ - --tx-body-file node-pool${N}/tx/tx${N}-protocol-config-funds.txbody \\ - --out-file node-pool${N}/tx/tx${N}-protocol-config-funds.tx - - ## Submit the transaction - CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction submit \\ - --tx-file node-pool${N}/tx/tx${N}-protocol-config-funds.tx \\ - --testnet-magic ${NETWORK_MAGIC} - - ## Compute the submitted transaction id - TX_ID_SUBMITTED=\$(CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction txid --tx-file node-pool${N}/tx/tx${N}-protocol-config-funds.tx) - if [[ "\${TX_ID_SUBMITTED}" =~ txhash ]]; then - TX_ID_SUBMITTED=\$(echo \$TX_ID_SUBMITTED | jq -r '.txhash') - fi - - ## Wait at least for 10 blocks so that the transaction is confirmed - wait_for_elapsed_blocks 10 - - ## Wait for all pool nodes to see the new transaction - for (( i=1; i<=${NUM_POOL_NODES}; i++ )); do - TOTAL_UTXOS_FOR_TX_ID=\$(CARDANO_NODE_SOCKET_PATH=node-pool\${i}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} query utxo \\ - --testnet-magic ${NETWORK_MAGIC} --address \$(cat addresses/${ADDR}.addr) --out-file /dev/stdout \\ - | jq '. | with_entries(select(.key | startswith("${TX_ID_SUBMITTED}"))) | length') - echo ">>>>>> Protocol configuration address funds retrieved on node-pool\${i}: \${TOTAL_UTXOS_FOR_TX_ID}" - if [ "\${TOTAL_UTXOS_FOR_TX_ID}" == "0" ]; then - touch ${MITHRIL_PROTOCOL_CONFIG_ERROR_FILE} - break - fi - done -} - -# Try to send funds to protocol configuration address -function try_send_funds_to_protocol_config_address { - SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS_MAX=10 - SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS=1 - SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUND_DELAY=2 - while true - do - send_funds_to_protocol_config_address - if [ -f ${MITHRIL_PROTOCOL_CONFIG_ERROR_FILE} ]; then - echo ">>>> Funds not transferred successfully to protocol configuration address, a rollback has happened [attempt \$SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS]!" - sleep \$SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUND_DELAY - else - echo ">>>> Funds transferred successfully to protocol configuration address [attempt \$SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS]!" - break - fi - SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS=\$(( \$SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS + 1 )) - if [ "\$SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS" -gt "\$SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS_MAX" ] ; then - echo ">>>> Timeout: Funds were not transferred successfully to protocol configuration address within \$SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS_MAX attempts" - exit 1 - fi - done -} - -# Write datums for protocol configuration address -function write_datums_for_protocol_config_address { - # Remove if exists previous error file - rm -f ${MITHRIL_PROTOCOL_CONFIG_ERROR_FILE} - - # Fetch transactions from UTxOs of the protocol configuration address - TX_IN_DATUM=\$(CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} query utxo \\ - --testnet-magic ${NETWORK_MAGIC} --address \$(cat addresses/${ADDR}.addr) --out-file /dev/stdout \\ - | jq -r 'to_entries | map({utxo: .key} + .value) | . [] | select(.inlineDatum | . != null and . != "") | .utxo') - TX_IN_NO_DATUM=\$(CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} query utxo \\ - --testnet-magic ${NETWORK_MAGIC} --address \$(cat addresses/${ADDR}.addr) --out-file /dev/stdout \\ - | jq -r 'to_entries | [last] | .[0].key') - - ## Build the transaction - if [ "\${TX_IN_DATUM}" == "" ]; then - CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction build \\ - --tx-in \${TX_IN_NO_DATUM} \\ - --tx-out \$(cat addresses/${ADDR}.addr)+${SCRIPT_TX_VALUE} \\ - --tx-out-inline-datum-file \${PROTOCOL_CONFIG_DATUM_FILE} \\ - --change-address \$(cat addresses/${ADDR}.addr) \\ - --testnet-magic ${NETWORK_MAGIC} \\ - --invalid-hereafter 100000 \\ - --out-file node-pool${N}/tx/tx${N}-protocol-configuration-datum.txbody - else - CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction build \\ - --tx-in \${TX_IN_DATUM} \\ - --tx-in \${TX_IN_NO_DATUM} \\ - --tx-out \$(cat addresses/${ADDR}.addr)+${SCRIPT_TX_VALUE} \\ - --tx-out-inline-datum-file \${PROTOCOL_CONFIG_DATUM_FILE} \\ - --change-address \$(cat addresses/${ADDR}.addr) \\ - --testnet-magic ${NETWORK_MAGIC} \\ - --invalid-hereafter 100000 \\ - --out-file node-pool${N}/tx/tx${N}-protocol-configuration-datum.txbody - fi - - ## Sign the transaction - CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction sign \\ - --signing-key-file addresses/${ADDR}.skey \\ - --testnet-magic ${NETWORK_MAGIC} \\ - --tx-body-file node-pool${N}/tx/tx${N}-protocol-configuration-datum.txbody \\ - --out-file node-pool${N}/tx/tx${N}-protocol-configuration-datum.tx - - ## Submit the transaction - CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction submit \\ - --tx-file node-pool${N}/tx/tx${N}-protocol-configuration-datum.tx \\ - --testnet-magic ${NETWORK_MAGIC} - - ## Wait at least for 10 blocks so that the transaction is confirmed - wait_for_elapsed_blocks 10 - - ## Wait for all pool nodes to see the new protocol configuration datum - for (( i=1; i<=${NUM_POOL_NODES}; i++ )); do - INLINE_DATUM=\$(CARDANO_NODE_SOCKET_PATH=node-pool\${i}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} query utxo \\ - --testnet-magic ${NETWORK_MAGIC} --address \$(cat addresses/${ADDR}.addr) --out-file /dev/stdout \\ - | jq -r '. [] | select(.inlineDatum | . != null and . != "") | .inlineDatum.fields[].bytes' | xxd -r -p | jq) - echo ">>>>>> Protocol configuration address inline datum retrieved on node-pool\${i}: \${INLINE_DATUM}" - if [ "\${INLINE_DATUM}" == "" ]; then - touch ${MITHRIL_PROTOCOL_CONFIG_ERROR_FILE} - break - fi - done -} - -# Try to write datums for protocol configuration address -function try_write_datums_for_protocol_config_address { - WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS_MAX=10 - WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS=1 - WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUND_DELAY=2 - while true - do - write_datums_for_protocol_config_address - if [ -f ${MITHRIL_PROTOCOL_CONFIG_ERROR_FILE} ]; then - echo ">>>> Datums not written successfully for protocol configuration address, a rollback has happened [attempt \$WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS]!" - sleep \$WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUND_DELAY - else - echo ">>>> Datums successfully written for protocol configuration address [attempt \$WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS]!" - break - fi - WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS=\$(( \$WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS + 1 )) - if [ "\$WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS" -gt "\$WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS_MAX" ] ; then - echo ">>>> Timeout: Datums were not written successfully for protocol configuration address within \$WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS_MAX attempts" - exit 1 - fi - done -} +. "cardano-chain.sh" # Try to send funds to protocol configuration address -try_send_funds_to_protocol_config_address +try_send_funds_to_address "${ADDR}" "${AMOUNT_TRANSFERRED}" "protocol-configuration" "${N}" # Try to write datums for protocol configuration address -try_write_datums_for_protocol_config_address +try_write_datums_for_address "${ADDR}" "${SCRIPT_TX_VALUE}" "protocol-configuration" "${N}" "\${PROTOCOL_CONFIG_DATUM_FILE}" EOF diff --git a/mithril-test-lab/mithril-end-to-end/Cargo.toml b/mithril-test-lab/mithril-end-to-end/Cargo.toml index 356b5387db0..80ae746e4ee 100644 --- a/mithril-test-lab/mithril-end-to-end/Cargo.toml +++ b/mithril-test-lab/mithril-end-to-end/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mithril-end-to-end" -version = "0.5.14" +version = "0.5.15" authors = { workspace = true } edition = { workspace = true } documentation = { workspace = true } diff --git a/mithril-test-lab/mithril-end-to-end/src/main.rs b/mithril-test-lab/mithril-end-to-end/src/main.rs index af9ef6528e4..1a43db67315 100644 --- a/mithril-test-lab/mithril-end-to-end/src/main.rs +++ b/mithril-test-lab/mithril-end-to-end/src/main.rs @@ -215,9 +215,9 @@ struct MithrilArgs { #[clap(long, default_value = "cardano-chain")] protocol_configuration_reader_adapter: String, - /// Signers read on-chain protocol configurations - #[clap(long, default_value_t = false)] - signers_read_on_chain_protocol_configurations: bool, + /// Protocol configuration reader adapter used by signers + #[clap(long, default_value = "cardano-chain")] + signer_protocol_configuration_reader_adapter: String, /// Aggregate signature type used to create the certificates #[clap(long, value_enum, default_value = "Concatenation")] @@ -534,6 +534,9 @@ impl App { protocol_configuration_reader_adapter: args .mithril .protocol_configuration_reader_adapter, + signer_protocol_configuration_reader_adapter: args + .mithril + .signer_protocol_configuration_reader_adapter, startup_protocol_configuration, signed_entity_types: scenario.signed_entity_types(), aggregate_signature_type: args.mithril.aggregate_signature_type, @@ -545,9 +548,6 @@ impl App { skip_signature_delayer: args.mithril.skip_signature_delayer, use_p2p_passive_relays, use_era_specific_work_dir: args.mithril.mithril_next_era.is_some(), - signers_read_on_chain_protocol_configurations: args - .mithril - .signers_read_on_chain_protocol_configurations, }, ) .await?, diff --git a/mithril-test-lab/mithril-end-to-end/src/mithril/aggregator.rs b/mithril-test-lab/mithril-end-to-end/src/mithril/aggregator.rs index fe204280fd5..2b8ae3e1a4f 100644 --- a/mithril-test-lab/mithril-end-to-end/src/mithril/aggregator.rs +++ b/mithril-test-lab/mithril-end-to-end/src/mithril/aggregator.rs @@ -7,7 +7,7 @@ use std::time::Duration; use anyhow::{Context, anyhow}; use mithril_common::entities::{ - CardanoBlocksTransactionsSigningConfig, CardanoTransactionsSigningConfig, + CardanoBlocksTransactionsSigningConfig, CardanoTransactionsSigningConfig, ProtocolParameters, }; use slog_scope::info; use tokio::process::Child; @@ -42,6 +42,7 @@ pub struct AggregatorConfig<'a> { pub mithril_era_marker_address: &'a str, pub protocol_configuration_reader_adapter: &'a str, pub protocol_configuration_marker_address: &'a str, + pub startup_protocol_parameters: &'a ProtocolParameters, pub signed_entity_types: &'a [String], pub aggregate_signature_type: AggregateSignatureType, pub chain_observer_type: &'a str, @@ -92,7 +93,11 @@ impl Aggregator { PROTOCOL_CONFIGURATION_MARKERS_VERIFICATION_KEY ) } else { - r#"{{"type": "fake"}}"#.to_string() + let protocol_parameters = aggregator_config.startup_protocol_parameters.clone(); + format!( + r#"{{"type":"fake","protocol_parameters":{{"k":{},"m":{},"phi_f":{}}}}}"#, + protocol_parameters.k, protocol_parameters.m, protocol_parameters.phi_f + ) }; let ancillary_files_signer_config = format!(r#"{{"type": "secret-key", "secret_key": "{ANCILLARY_MANIFEST_SECRET_KEY}"}}"#); @@ -455,6 +460,18 @@ impl Aggregator { ); } + pub async fn set_fake_protocol_configuration_reader_adapter_config( + &self, + protocol_parameters: &entities::ProtocolParameters, + ) { + let mut command = self.command.write().await; + let fake_config = format!( + r#"{{"type": "fake", "protocol_parameters":{{"k":{},"m":{},"phi_f":{}}}}}"#, + protocol_parameters.k, protocol_parameters.m, protocol_parameters.phi_f + ); + command.set_env_var("PROTOCOL_CONFIGURATION_READER_ADAPTER_CONFIG", &fake_config); + } + pub async fn set_signing_config( &self, cardano_transaction_signing_config: Option, diff --git a/mithril-test-lab/mithril-end-to-end/src/mithril/infrastructure.rs b/mithril-test-lab/mithril-end-to-end/src/mithril/infrastructure.rs index 66178919025..b5e1204648b 100644 --- a/mithril-test-lab/mithril-end-to-end/src/mithril/infrastructure.rs +++ b/mithril-test-lab/mithril-end-to-end/src/mithril/infrastructure.rs @@ -46,6 +46,7 @@ pub struct MithrilInfrastructureConfig { pub mithril_era: String, pub mithril_era_reader_adapter: String, pub protocol_configuration_reader_adapter: String, + pub signer_protocol_configuration_reader_adapter: String, pub startup_protocol_configuration: ProtocolConfiguration, pub signed_entity_types: Vec, pub aggregate_signature_type: AggregateSignatureType, @@ -57,7 +58,6 @@ pub struct MithrilInfrastructureConfig { pub use_dmq: bool, pub dmq_node_flavor: Option, pub use_era_specific_work_dir: bool, - pub signers_read_on_chain_protocol_configurations: bool, } impl MithrilInfrastructureConfig { @@ -87,6 +87,7 @@ impl MithrilInfrastructureConfig { mithril_era: "era1".to_string(), mithril_era_reader_adapter: "adapter1".to_string(), protocol_configuration_reader_adapter: "adapter1".to_string(), + signer_protocol_configuration_reader_adapter: "adapter1".to_string(), startup_protocol_configuration: ProtocolConfiguration { epoch: Epoch(42), protocol_parameters: ProtocolParameters::new(10, 20, 0.123), @@ -106,7 +107,6 @@ impl MithrilInfrastructureConfig { use_dmq: false, dmq_node_flavor: Some(DmqNodeFlavor::Fake), use_era_specific_work_dir: false, - signers_read_on_chain_protocol_configurations: false, } } } @@ -194,6 +194,7 @@ impl MithrilInfrastructure { leader_aggregator.endpoint(), signer_cardano_nodes, &relay_signers, + leader_aggregator.is_reading_protocol_configurations_on_chain(), ) .await?; @@ -339,6 +340,7 @@ impl MithrilInfrastructure { protocol_configuration_marker_address: &config .devnet .protocol_configuration_marker_address()?, + startup_protocol_parameters: &config.startup_protocol_configuration.protocol_parameters, signed_entity_types: &config.signed_entity_types, aggregate_signature_type: config.aggregate_signature_type, chain_observer_type, @@ -465,6 +467,7 @@ impl MithrilInfrastructure { leader_aggregator_endpoint: String, pool_nodes: &[PoolNode], relay_signers: &[RelaySigner], + protocol_configurations_written_on_chain: bool, ) -> StdResult> { let mut signers: Vec = vec![]; @@ -479,6 +482,13 @@ impl MithrilInfrastructure { leader_aggregator_endpoint.clone() }; + let protocol_configuration_reader_adapter = if protocol_configurations_written_on_chain + { + &config.signer_protocol_configuration_reader_adapter + } else { + "http" + }; + let signer = Signer::new(&SignerConfig { signer_number: index + 1, aggregator_endpoint, @@ -491,8 +501,7 @@ impl MithrilInfrastructure { mithril_era: &config.mithril_era, mithril_era_reader_adapter: &config.mithril_era_reader_adapter, mithril_era_marker_address: &config.devnet.mithril_era_marker_address()?, - protocol_configuration_reader_adapter: &config - .protocol_configuration_reader_adapter, + protocol_configuration_reader_adapter, protocol_configuration_marker_address: &config .devnet .protocol_configuration_marker_address()?, @@ -500,8 +509,6 @@ impl MithrilInfrastructure { skip_signature_delayer: config.skip_signature_delayer, use_dmq: config.use_dmq, dmq_node_flavor: &config.dmq_node_flavor, - read_on_chain_protocol_configurations: config - .signers_read_on_chain_protocol_configurations, })?; signer.start().await?; diff --git a/mithril-test-lab/mithril-end-to-end/src/mithril/signer.rs b/mithril-test-lab/mithril-end-to-end/src/mithril/signer.rs index 68e7929d6f3..27f8eda7cfc 100644 --- a/mithril-test-lab/mithril-end-to-end/src/mithril/signer.rs +++ b/mithril-test-lab/mithril-end-to-end/src/mithril/signer.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use std::path::Path; use std::sync::Arc; @@ -10,7 +9,7 @@ use tokio::process::Child; use tokio::sync::RwLock; use crate::devnet::PoolNode; -use crate::utils::{MithrilCommand, NodeVersion}; +use crate::utils::{EnvVars, MithrilCommand, NodeVersion}; use crate::{ DEVNET_DMQ_MAGIC_ID, DEVNET_MAGIC_ID, DmqNodeFlavor, ERA_MARKERS_VERIFICATION_KEY, PROTOCOL_CONFIGURATION_MARKERS_VERIFICATION_KEY, @@ -35,7 +34,6 @@ pub struct SignerConfig<'a> { pub skip_signature_delayer: bool, pub use_dmq: bool, pub dmq_node_flavor: &'a Option, - pub read_on_chain_protocol_configurations: bool, } #[derive(Debug)] @@ -68,23 +66,13 @@ impl Signer { signer_config.mithril_era ) }; - let protocol_configuration_reader_adapter_config = - if signer_config.protocol_configuration_reader_adapter == "cardano-chain" { - format!( - r#"{{"type": "cardano-chain", "address": "{}", "verification_key": "{}"}}"#, - signer_config.protocol_configuration_marker_address, - PROTOCOL_CONFIGURATION_MARKERS_VERIFICATION_KEY - ) - } else { - r#"{{"type": "fake"}}"#.to_string() - }; let mithril_run_interval = format!("{}", signer_config.mithril_run_interval); let skip_signature_delayer = if signer_config.skip_signature_delayer { "true" } else { "false" }; - let mut env = HashMap::from([ + let mut env = EnvVars::from([ ("NETWORK", "devnet"), ("NETWORK_MAGIC", &magic_id), ("DMQ_NETWORK_MAGIC", &dmq_magic_id), @@ -124,11 +112,15 @@ impl Signer { ("PARTY_ID", &party_id), ]); if Self::can_read_protocol_configurations_on_chain(&version) - && signer_config.read_on_chain_protocol_configurations + && signer_config.protocol_configuration_reader_adapter == "cardano-chain" { env.insert( "PROTOCOL_CONFIGURATION_READER_ADAPTER_CONFIG", - &protocol_configuration_reader_adapter_config, + format!( + r#"{{"type": "cardano-chain", "address": "{}", "verification_key": "{}"}}"#, + signer_config.protocol_configuration_marker_address, + PROTOCOL_CONFIGURATION_MARKERS_VERIFICATION_KEY + ), ); } if signer_config.enable_certification { diff --git a/mithril-test-lab/mithril-end-to-end/src/stress_test/aggregator_helpers.rs b/mithril-test-lab/mithril-end-to-end/src/stress_test/aggregator_helpers.rs index a84ba2f66c4..adc519b1a63 100644 --- a/mithril-test-lab/mithril-end-to-end/src/stress_test/aggregator_helpers.rs +++ b/mithril-test-lab/mithril-end-to-end/src/stress_test/aggregator_helpers.rs @@ -42,6 +42,7 @@ pub async fn bootstrap_aggregator( mithril_era_reader_adapter: "dummy", protocol_configuration_marker_address: "", protocol_configuration_reader_adapter: "dummy", + startup_protocol_parameters: &signers_fixture.protocol_parameters(), signed_entity_types: &signed_entity_types, aggregate_signature_type: AggregateSignatureType::Concatenation, chain_observer_type, diff --git a/mithril-test-lab/mithril-end-to-end/src/utils/mithril_command.rs b/mithril-test-lab/mithril-end-to-end/src/utils/mithril_command.rs index 1c6d5a18660..e53ad33dd18 100644 --- a/mithril-test-lab/mithril-end-to-end/src/utils/mithril_command.rs +++ b/mithril-test-lab/mithril-end-to-end/src/utils/mithril_command.rs @@ -1,6 +1,7 @@ use anyhow::{Context, anyhow}; use slog_scope::info; use std::collections::HashMap; +use std::ffi::OsString; use std::path::{Path, PathBuf}; use tokio::process::{Child, Command}; @@ -15,24 +16,65 @@ pub struct MithrilCommand { log_path: PathBuf, output_path: Option, work_dir: PathBuf, - env_vars: HashMap, + env_vars: EnvVars, default_args: Vec, } +#[derive(Debug, Clone, Default)] +pub struct EnvVars(HashMap); + +impl EnvVars { + pub fn new() -> Self { + Self::default() + } + + pub fn with(mut self, name: impl Into, value: impl Into) -> Self { + self.0.insert(name.into(), value.into()); + self + } + + pub fn insert(&mut self, name: impl Into, value: impl Into) { + self.0.insert(name.into(), value.into()); + } + + pub fn unset(&mut self, name: impl Into) { + self.0.remove(&name.into()); + } +} + +impl From<[(&str, &str); N]> for EnvVars { + fn from(arr: [(&str, &str); N]) -> Self { + Self(arr.into_iter().map(|(k, v)| (k.into(), v.into())).collect()) + } +} + +impl From> for EnvVars { + fn from(value: HashMap<&str, &str>) -> Self { + Self(value.into_iter().map(|(k, v)| (k.into(), v.into())).collect()) + } +} + +impl<'a> IntoIterator for &'a EnvVars { + type Item = (&'a OsString, &'a OsString); + type IntoIter = std::collections::hash_map::Iter<'a, OsString, OsString>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + impl MithrilCommand { - pub fn new( + pub fn new>( name: &str, work_dir: &Path, bin_dir: &Path, - env_vars: HashMap<&str, &str>, + env_vars: EV, default_args: &[&str], ) -> StdResult { let process_path = file_utils::get_process_path(name, bin_dir)?; let log_path = work_dir.join(format!("{name}.log")); - // ugly but it's far easier for callers to manipulate string literals - let mut env_vars: HashMap = - env_vars.iter().map(|(k, v)| (k.to_string(), v.to_string())).collect(); + let mut env_vars = env_vars.into(); let default_args = default_args.iter().map(|s| s.to_string()).collect(); env_vars.insert("RUST_BACKTRACE".to_string(), "full".to_string()); @@ -74,7 +116,7 @@ impl MithrilCommand { } pub fn unset_env_var(&mut self, name: &str) { - self.env_vars.remove(name); + self.env_vars.unset(name); } pub fn start(&mut self, args: &[String]) -> StdResult { diff --git a/mithril-test-lab/mithril-end-to-end/src/utils/mod.rs b/mithril-test-lab/mithril-end-to-end/src/utils/mod.rs index 3804b93e75b..4d0da68d170 100644 --- a/mithril-test-lab/mithril-end-to-end/src/utils/mod.rs +++ b/mithril-test-lab/mithril-end-to-end/src/utils/mod.rs @@ -12,7 +12,7 @@ pub use child_logger::*; pub use compatibility_checker::*; pub use formatting::*; pub use immutable_files_utils::*; -pub use mithril_command::MithrilCommand; +pub use mithril_command::{EnvVars, MithrilCommand}; pub use spec_utils::{AttemptResult, Backoff, TimeoutReason}; pub use version_req::NodeVersion;