diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index 07475ae4472..6f03b297949 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -121,7 +121,7 @@ jobs: - name: Build E2E runner shell: bash run: | - cargo build --release --bin mithril-end-to-end + cargo build --release --bin mithril-end-to-end ${{ inputs.additional-nodes-build-args }} - name: Upload E2E runner uses: actions/upload-artifact@v7 @@ -129,33 +129,67 @@ jobs: name: e2e-runner path: target/release/mithril-end-to-end - minimal-e2e: + e2e: runs-on: ubuntu-24.04 needs: [prepare-env-variables, prepare-binaries, prepare-e2e-runner] strategy: fail-fast: false matrix: cardano_node_version: ${{ fromJSON(needs.prepare-env-variables.outputs.cardano_node_versions) }} - aggregate_signature_type: ["Concatenation"] # Todo: add "Snark" once the snark certificate chain is ready - extra_args: ["--cardano-slot-length 1.0 --cardano-epoch-length 60"] + mode: + [ + "minimal-concatenation", + "minimal-snark", + "minimal-ivc-snark", + "full-concatenation", + ] + include: + - mode: "minimal-concatenation" + aggregate_signature_type: "Concatenation" + run_args: "--cardano-slot-length 1.0 --cardano-epoch-length 60" + scenario_args: "minimal --signed-entity-type ${{ inputs.minimal-scenario-signed-entity-types }}" + timeout_minutes: 120 + skip: false + - mode: "minimal-snark" + aggregate_signature_type: "Snark" + run_args: "--mithril-era=lagrange --cardano-slot-length 1.0 --cardano-epoch-length 600 --mithril-run-interval 60000" + scenario_args: "minimal --signed-entity-type ${{ inputs.minimal-scenario-signed-entity-types }}" + timeout_minutes: 100 + skip: false + - mode: "minimal-ivc-snark" + aggregate_signature_type: "IvcSnark" + run_args: "--mithril-era=lagrange --cardano-slot-length 1.0 --cardano-epoch-length 900 --mithril-run-interval 120000" + scenario_args: "minimal --signed-entity-type ${{ inputs.minimal-scenario-signed-entity-types }}" + timeout_minutes: 150 + skip: false + - mode: "full-concatenation" + aggregate_signature_type: "Concatenation" + run_args: "--cardano-slot-length 1.0 --cardano-epoch-length 60" + scenario_args: "full --signed-entity-types ${{ inputs.full-scenario-signed-entity-types }}" + timeout_minutes: 350 + skip: false steps: - name: Checkout + if: ${{ !matrix.skip }} uses: actions/checkout@v6 - name: Download binaries + if: ${{ !matrix.skip }} uses: actions/download-artifact@v8 with: name: mithril-binaries path: ./mithril-binaries - name: Download e2e runner + if: ${{ !matrix.skip }} uses: actions/download-artifact@v8 with: name: e2e-runner path: ./mithril-binaries - name: Prepare binaries + if: ${{ !matrix.skip }} shell: bash run: | chmod +x ./mithril-binaries/mithril-aggregator @@ -166,12 +200,13 @@ jobs: mkdir -p artifacts - name: Run E2E tests + if: ${{ !matrix.skip }} uses: nick-fields/retry@v4 with: shell: bash max_attempts: 3 retry_on_exit_code: 2 - timeout_minutes: 120 + timeout_minutes: ${{ matrix.timeout_minutes }} warning_on_retry: true command: | ./mithril-binaries/mithril-end-to-end -vvv \ @@ -180,7 +215,7 @@ jobs: --devnet-scripts-directory=./mithril-test-lab/cardano-devnet \ --cardano-node-version ${{ matrix.cardano_node_version }} \ --aggregate-signature-type ${{ matrix.aggregate_signature_type }} \ - ${{ matrix.extra_args }} minimal --signed-entity-type ${{ inputs.minimal-scenario-signed-entity-types }} + ${{ matrix.run_args }} ${{ matrix.scenario_args }} EXIT_CODE=$? if [ $EXIT_CODE -eq 0 ]; then @@ -197,84 +232,7 @@ jobs: if: ${{ failure() }} uses: actions/upload-artifact@v7 with: - name: mithril-e2e-tests-artifacts-run_${{ github.run_number }}-attempt_${{ github.run_attempt }}-sig_type_${{ matrix.aggregate_signature_type }}-scenario_minimal-cardano-${{ matrix.cardano_node_version }} - path: | - ./artifacts/* - # including node.sock makes the upload fails so exclude them: - !./artifacts/**/node.sock - # exclude cardano tools, saving ~500mb of data: - !./artifacts/devnet/bin/ - if-no-files-found: error - - full-e2e: - runs-on: ubuntu-24.04 - needs: [prepare-env-variables, prepare-binaries, prepare-e2e-runner] - strategy: - fail-fast: false - matrix: - cardano_node_version: ${{ fromJSON(needs.prepare-env-variables.outputs.cardano_node_versions) }} - aggregate_signature_type: ["Concatenation"] - extra_args: ["--cardano-slot-length 1.0 --cardano-epoch-length 60"] - - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Download binaries - uses: actions/download-artifact@v8 - with: - name: mithril-binaries - path: ./mithril-binaries - - - name: Download e2e runner - uses: actions/download-artifact@v8 - with: - name: e2e-runner - path: ./mithril-binaries - - - name: Prepare binaries - shell: bash - run: | - chmod +x ./mithril-binaries/mithril-aggregator - chmod +x ./mithril-binaries/mithril-client - chmod +x ./mithril-binaries/mithril-signer - chmod +x ./mithril-binaries/mithril-relay - chmod +x ./mithril-binaries/mithril-end-to-end - mkdir -p artifacts - - - name: Run E2E tests - uses: nick-fields/retry@v4 - with: - shell: bash - max_attempts: 3 - retry_on_exit_code: 2 - timeout_minutes: 120 - warning_on_retry: true - command: | - ./mithril-binaries/mithril-end-to-end -vvv \ - --bin-directory ./mithril-binaries/ \ - --work-directory=./artifacts \ - --devnet-scripts-directory=./mithril-test-lab/cardano-devnet \ - --cardano-node-version ${{ matrix.cardano_node_version }} \ - --aggregate-signature-type ${{ matrix.aggregate_signature_type }} \ - ${{ matrix.extra_args }} full --signed-entity-types ${{ inputs.full-scenario-signed-entity-types }} - - EXIT_CODE=$? - if [ $EXIT_CODE -eq 0 ]; then - echo "RESULT=success" >> $GITHUB_ENV - elif [ $EXIT_CODE -eq 3 ]; then - echo "RESULT=incompatible" >> $GITHUB_ENV - exit 0 - else - echo "RESULT=failure" >> $GITHUB_ENV - fi - exit $EXIT_CODE - - - name: Upload E2E Tests Artifacts - if: ${{ failure() }} - uses: actions/upload-artifact@v7 - with: - name: mithril-e2e-tests-artifacts-run_${{ github.run_number }}-attempt_${{ github.run_attempt }}-sig_type_${{ matrix.aggregate_signature_type }}-scenario_full-cardano-${{ matrix.cardano_node_version }} + name: mithril-e2e-tests-artifacts-run_${{ github.run_number }}-attempt_${{ github.run_attempt }}-mode_${{ matrix.mode }}-cardano-${{ matrix.cardano_node_version }} path: | ./artifacts/* # including node.sock makes the upload fails so exclude them: diff --git a/mithril-aggregator/src/commands/serve_command.rs b/mithril-aggregator/src/commands/serve_command.rs index 90977754abb..a9906fa57a8 100644 --- a/mithril-aggregator/src/commands/serve_command.rs +++ b/mithril-aggregator/src/commands/serve_command.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "future_snark")] +use std::time::Instant; use std::{ collections::HashMap, net::IpAddr, @@ -20,6 +22,11 @@ use mithril_cli_helper::{ }; use mithril_common::StdResult; use mithril_common::entities::SignedEntityTypeDiscriminants; +#[cfg(feature = "future_snark")] +use mithril_common::{ + AggregateSignatureType, + crypto_helper::{ProtocolParameters as ProverProtocolParameters, SnarkProverSetupWarmer}, +}; use mithril_doc::{Documenter, DocumenterDefault, StructDoc}; use mithril_metric::MetricsServer; @@ -118,6 +125,84 @@ impl Source for ServeCommand { } impl ServeCommand { + /// Materializes the aggregate signature prover setup in the background, so the first signing + /// round does not pay it inside its aggregation. + /// + /// Best effort: on any failure the setup is materialized on first use, as it would be without + /// this. A concatenation aggregate signature has no setup to materialize. + #[cfg(feature = "future_snark")] + async fn warm_aggregate_signature_prover( + dependencies_builder: &mut DependenciesBuilder, + configuration: &ServeCommandConfiguration, + logger: &Logger, + ) { + let aggregate_signature_type = configuration.aggregate_signature_type; + if aggregate_signature_type == AggregateSignatureType::Concatenation { + return; + } + + let protocol_parameters = match Self::protocol_parameters_for_aggregation( + dependencies_builder, + ) + .await + { + Ok(protocol_parameters) => protocol_parameters, + Err(error) => { + warn!( + logger, + "Could not read the protocol parameters to precompute the aggregate signature prover"; + "error" => ?error + ); + return; + } + }; + + info!( + logger, + "Precomputing the aggregate signature prover, which takes a few minutes; the first \ + signing round waits for it if it is not finished by then"; + "aggregate_signature_type" => %aggregate_signature_type + ); + let logger = logger.clone(); + + tokio::task::spawn_blocking(move || { + let started_at = Instant::now(); + match SnarkProverSetupWarmer::warm(&protocol_parameters, aggregate_signature_type) { + Ok(()) => info!( + logger, "Aggregate signature prover precomputed"; + "elapsed_seconds" => started_at.elapsed().as_secs() + ), + Err(error) => warn!( + logger, "Failed to precompute the aggregate signature prover"; + "error" => ?error + ), + } + }); + } + + /// Protocol parameters the current epoch aggregates with. + #[cfg(feature = "future_snark")] + async fn protocol_parameters_for_aggregation( + dependencies_builder: &mut DependenciesBuilder, + ) -> StdResult { + let epoch = dependencies_builder + .get_ticker_service() + .await? + .get_current_epoch() + .await?; + let network_configuration = dependencies_builder + .get_mithril_network_configuration_provider() + .await? + .get_network_configuration(epoch) + .await?; + + Ok(ProverProtocolParameters::from( + network_configuration + .configuration_for_aggregation + .protocol_parameters, + )) + } + pub async fn execute( &self, root_logger: Logger, @@ -160,6 +245,10 @@ impl ServeCommand { ) .await?; + #[cfg(feature = "future_snark")] + Self::warm_aggregate_signature_prover(&mut dependencies_builder, &config, &root_logger) + .await; + // Start the aggregator runtime let mut runtime = dependencies_builder .create_aggregator_runner() diff --git a/mithril-common/src/crypto_helper/mod.rs b/mithril-common/src/crypto_helper/mod.rs index 2cbfe120f8e..31c6d49f6b0 100644 --- a/mithril-common/src/crypto_helper/mod.rs +++ b/mithril-common/src/crypto_helper/mod.rs @@ -29,6 +29,8 @@ pub use mithril_merkle_tree::{ Bytes, MKMap, MKMapKey, MKMapNode, MKMapProof, MKMapValue, MKProof, MKTree, MKTreeLeafIndexer, MKTreeLeafPosition, MKTreeNode, MKTreeStoreInMemory, MKTreeStorer, }; +#[cfg(feature = "future_snark")] +pub use mithril_stm::SnarkProverSetupWarmer; pub use types::*; /// The current protocol version diff --git a/mithril-stm/src/lib.rs b/mithril-stm/src/lib.rs index 8b60fa824c2..d04621c857e 100644 --- a/mithril-stm/src/lib.rs +++ b/mithril-stm/src/lib.rs @@ -75,7 +75,8 @@ pub use circuits::{CIRCUIT_VERIFICATION_KEY_DIGEST_SIZE, CircuitVerificationKeyD #[cfg(feature = "future_snark")] pub use proof_system::{ - AggregateVerificationKeyForSnark, MERKLE_TREE_DEPTH_FOR_SNARK, SnarkProof, SnarkVerifierData, + AggregateVerificationKeyForSnark, MERKLE_TREE_DEPTH_FOR_SNARK, SnarkProof, + SnarkProverSetupWarmer, SnarkVerifierData, }; #[cfg(feature = "future_snark")] diff --git a/mithril-stm/src/proof_system/mod.rs b/mithril-stm/src/proof_system/mod.rs index 55ac90fce1e..826ae85abd0 100644 --- a/mithril-stm/src/proof_system/mod.rs +++ b/mithril-stm/src/proof_system/mod.rs @@ -23,6 +23,8 @@ mod halo2_snark; mod snark_prover_factory; #[cfg(feature = "future_snark")] mod snark_setup_cache; +#[cfg(feature = "future_snark")] +mod snark_setup_warmer; /// Serialized `ParamsVerifierKZG` (i.e. `s_g2`) from the Midnight production trusted SRS. /// @@ -80,3 +82,5 @@ pub(crate) use snark_prover_factory::MockSnarkProverFactory; pub(crate) use snark_prover_factory::{NonDeterministicSnarkProverFactory, SnarkProverFactory}; #[cfg(feature = "future_snark")] pub(crate) use snark_setup_cache::SnarkProverSetupReuse; +#[cfg(feature = "future_snark")] +pub use snark_setup_warmer::SnarkProverSetupWarmer; diff --git a/mithril-stm/src/proof_system/snark_setup_warmer.rs b/mithril-stm/src/proof_system/snark_setup_warmer.rs new file mode 100644 index 00000000000..6be6932c3c3 --- /dev/null +++ b/mithril-stm/src/proof_system/snark_setup_warmer.rs @@ -0,0 +1,54 @@ +//! Ahead-of-time materialization of the SNARK prover setups. +//! +//! A node materializes its prover setup on the first aggregation it runs, which is the longest part +//! of a cold start and lands inside a signing round. [`SnarkProverSetupWarmer`] does it beforehand, +//! into the same process-wide cache the provers read, so the first aggregation finds it ready. + +use crate::{ + AggregateSignatureType, MERKLE_TREE_DEPTH_FOR_SNARK, Parameters, StmResult, + proof_system::snark_setup_cache::SnarkProverSetupReuse, +}; + +/// Materializes the SNARK prover setups a node needs, ahead of its first aggregation. +pub struct SnarkProverSetupWarmer; + +impl SnarkProverSetupWarmer { + /// Materializes the setups `aggregate_signature_type` needs for `parameters` into the cache the + /// provers read, deriving and storing their keys on a miss. Nothing to materialize for a + /// concatenation aggregate signature. + /// + /// The setups are kept resident, since the process calling this is the one that will aggregate. + pub fn warm( + parameters: &Parameters, + aggregate_signature_type: AggregateSignatureType, + ) -> StmResult<()> { + match aggregate_signature_type { + AggregateSignatureType::Concatenation => (), + AggregateSignatureType::Snark => { + SnarkProverSetupReuse::Enabled + .certificate_setup(parameters, MERKLE_TREE_DEPTH_FOR_SNARK)?; + } + AggregateSignatureType::IvcSnark => { + SnarkProverSetupReuse::Enabled.ivc_setup(parameters)?; + } + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn concatenation_has_no_setup_to_derive() { + let parameters = Parameters { + m: 9, + k: 5, + phi_f: 0.95, + }; + + SnarkProverSetupWarmer::warm(¶meters, AggregateSignatureType::Concatenation).unwrap(); + } +} 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 4f07eca3feb..7448292a876 100644 --- a/mithril-test-lab/mithril-end-to-end/src/main.rs +++ b/mithril-test-lab/mithril-end-to-end/src/main.rs @@ -31,7 +31,7 @@ use mithril_end_to_end::scenario::{FullScenario, MinimalScenario, RunOnlyScenari use mithril_end_to_end::toolkit::{ScenarioToolkit, ScenarioToolkitContext}; use mithril_end_to_end::{ AggregateSignatureType, Aggregator, Client, CompatibilityChecker, CompatibilityCheckerError, - Devnet, DevnetBootstrapArgs, DmqNodeFlavor, IpfsDevnet, IpfsDevnetBootstrapArgs, + Devnet, DevnetBootstrapArgs, DmqNodeFlavor, GenesisKeys, IpfsDevnet, IpfsDevnetBootstrapArgs, IpfsDevnetMode, MithrilInfrastructure, MithrilInfrastructureConfig, NodeVersion, ProtocolConfiguration, RelaySigner, RetryableDevnetError, Signer, }; @@ -554,6 +554,13 @@ impl App { let startup_protocol_configuration = Self::build_startup_protocol_configuration(&args.mithril.aggregate_signature_type); + let genesis_keys = GenesisKeys::select( + [args.mithril.mithril_era.as_str()] + .into_iter() + .chain(args.mithril.mithril_next_era.as_deref()), + &NodeVersion::fetch(Aggregator::BIN_NAME, &args.bin_directory)?, + &NodeVersion::fetch(Client::BIN_NAME, &args.bin_directory)?, + ); let infrastructure = Arc::new( MithrilInfrastructure::start( @@ -581,6 +588,7 @@ impl App { startup_protocol_configuration, signed_entity_types: scenario.signed_entity_types(), aggregate_signature_type: args.mithril.aggregate_signature_type, + genesis_keys, use_dmq, dmq_node_flavor: args.network_topology.dmq_node_flavor, use_relays, 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 bb91ae19477..87ae044b904 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 @@ -18,10 +18,9 @@ use mithril_common::{CardanoNetwork, StdResult, entities}; use crate::utils::{EnvVars, MithrilCommand, NodeVersion}; use crate::{ ANCILLARY_MANIFEST_SECRET_KEY, AggregateSignatureType, DEVNET_DMQ_MAGIC_ID, DEVNET_MAGIC_ID, - DmqNodeFlavor, ERA_MARKERS_SECRET_KEY, ERA_MARKERS_VERIFICATION_KEY, FullNode, - GENESIS_SECRET_KEY, GENESIS_VERIFICATION_KEY, KuboNode, - PROTOCOL_CONFIGURATION_MARKERS_SECRET_KEY, PROTOCOL_CONFIGURATION_MARKERS_VERIFICATION_KEY, - RetryableDevnetError, + DmqNodeFlavor, ERA_MARKERS_SECRET_KEY, ERA_MARKERS_VERIFICATION_KEY, FullNode, GenesisKeys, + KuboNode, PROTOCOL_CONFIGURATION_MARKERS_SECRET_KEY, + PROTOCOL_CONFIGURATION_MARKERS_VERIFICATION_KEY, RetryableDevnetError, }; #[derive(Debug)] @@ -48,6 +47,7 @@ pub struct AggregatorConfig<'a> { pub aggregate_signature_type: AggregateSignatureType, pub chain_observer_type: &'a str, pub leader_aggregator_endpoint: &'a Option, + pub genesis_keys: GenesisKeys, pub use_dmq: bool, pub dmq_node_flavor: &'a Option, } @@ -134,8 +134,14 @@ impl Aggregator { "CARDANO_CLI_PATH", aggregator_config.cardano_cli_path.to_str().unwrap(), ), - ("GENESIS_VERIFICATION_KEY", GENESIS_VERIFICATION_KEY), - ("GENESIS_SECRET_KEY", GENESIS_SECRET_KEY), + ( + "GENESIS_VERIFICATION_KEY", + aggregator_config.genesis_keys.verification_key, + ), + ( + "GENESIS_SECRET_KEY", + aggregator_config.genesis_keys.secret_key, + ), ( "ERA_READER_ADAPTER_TYPE", aggregator_config.mithril_era_reader_adapter, diff --git a/mithril-test-lab/mithril-end-to-end/src/mithril/client.rs b/mithril-test-lab/mithril-end-to-end/src/mithril/client.rs index 3cb527d6d9f..659d55dcd48 100644 --- a/mithril-test-lab/mithril-end-to-end/src/mithril/client.rs +++ b/mithril-test-lab/mithril-end-to-end/src/mithril/client.rs @@ -8,7 +8,7 @@ use mithril_common::StdResult; use mithril_common::entities::{BlockHash, EpochSpecifier, TransactionHash}; use crate::utils::{MithrilCommand, NodeVersion}; -use crate::{ANCILLARY_MANIFEST_VERIFICATION_KEY, GENESIS_VERIFICATION_KEY}; +use crate::{ANCILLARY_MANIFEST_VERIFICATION_KEY, GenesisKeys}; #[derive(Debug)] pub struct Client { @@ -429,9 +429,14 @@ impl ClientCommand { impl Client { pub const BIN_NAME: &'static str = "mithril-client"; - pub fn new(aggregator_endpoint: String, work_dir: &Path, bin_dir: &Path) -> StdResult { + pub fn new( + aggregator_endpoint: String, + work_dir: &Path, + bin_dir: &Path, + genesis_keys: GenesisKeys, + ) -> StdResult { let env = HashMap::from([ - ("GENESIS_VERIFICATION_KEY", GENESIS_VERIFICATION_KEY), + ("GENESIS_VERIFICATION_KEY", genesis_keys.verification_key), ("AGGREGATOR_ENDPOINT", &aggregator_endpoint), ( "ANCILLARY_VERIFICATION_KEY", diff --git a/mithril-test-lab/mithril-end-to-end/src/mithril/genesis_keys.rs b/mithril-test-lab/mithril-end-to-end/src/mithril/genesis_keys.rs new file mode 100644 index 00000000000..e9e112ef5cc --- /dev/null +++ b/mithril-test-lab/mithril-end-to-end/src/mithril/genesis_keys.rs @@ -0,0 +1,137 @@ +use mithril_common::entities::SupportedEra; + +use crate::utils::NodeVersion; + +const LEGACY_VERIFICATION_KEY: &str = "5b33322c3235332c3138362c3230312c3137372c31312c3131372c3133352c3138372c3136372c3138312c3138382c32322c35392c3230362c3130352c3233312c3135302c3231352c33302c37382c3231322c37362c31362c3235322c3138302c37322c3133342c3133372c3234372c3136312c36385d"; +const LEGACY_SECRET_KEY: &str = "5b3131382c3138342c3232342c3137332c3136302c3234312c36312c3134342c36342c39332c3130362c3232392c38332c3133342c3138392c34302c3138392c3231302c32352c3138342c3136302c3134312c3233372c32362c3136382c35342c3233392c3230342c3133392c3131392c31332c3139395d"; +#[cfg(feature = "future_snark")] +const DUAL_VERIFICATION_KEY: &str = "012020fdbac9b10b7587bba7b5bc163bce69e796d71e4ed44c10fcb4488689f7a1444069c10d42f944c2a7a5138391aafe3dbe1e40e8516a9e174aff16dddfe5c3f303e65a3fa2187e10860541ff2dfc59296f15833b6ba549ff83153f9cde07d0eb26"; +#[cfg(feature = "future_snark")] +const DUAL_SECRET_KEY: &str = "012076b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7202c9dc80e956510c4461bdc39bda7e96a88a3f7a4635ff5e66e3040b5baa08e04"; + +/// First aggregator version accepting a dual genesis key bundle +const MIN_AGGREGATOR_VERSION_WITH_DUAL_KEYS: &str = "0.9.6"; + +/// First client version accepting a dual genesis key bundle +const MIN_CLIENT_VERSION_WITH_DUAL_KEYS: &str = "0.13.15"; + +/// Genesis keys handed to the nodes +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct GenesisKeys { + /// Hex encoded genesis verification key + pub verification_key: &'static str, + + /// Hex encoded genesis secret key + pub secret_key: &'static str, +} + +impl GenesisKeys { + /// Legacy Ed25519 keys, accepted by every node version + pub const LEGACY: Self = Self { + verification_key: LEGACY_VERIFICATION_KEY, + secret_key: LEGACY_SECRET_KEY, + }; + + /// Dual Ed25519 and Schnorr key bundles sharing the legacy Ed25519 keys, needed to sign a + /// Lagrange genesis certificate on nodes built with the SNARK feature + #[cfg(feature = "future_snark")] + pub const DUAL: Self = Self { + verification_key: DUAL_VERIFICATION_KEY, + secret_key: DUAL_SECRET_KEY, + }; + + /// Select the dual key bundles when a Lagrange era is run on nodes accepting them, which + /// requires the SNARK feature since the bundle format is not recognized without it, and + /// the legacy keys otherwise + pub fn select<'a>( + mithril_eras: impl IntoIterator, + aggregator_version: &NodeVersion, + client_version: &NodeVersion, + ) -> Self { + let runs_lagrange = mithril_eras + .into_iter() + .any(|era| era.eq_ignore_ascii_case(&SupportedEra::Lagrange.to_string())); + let nodes_accept_dual_keys = aggregator_version + .is_above_or_equal(MIN_AGGREGATOR_VERSION_WITH_DUAL_KEYS) + && client_version.is_above_or_equal(MIN_CLIENT_VERSION_WITH_DUAL_KEYS); + + Self::select_from_support(runs_lagrange && nodes_accept_dual_keys) + } + + #[cfg(feature = "future_snark")] + fn select_from_support(dual_keys_supported: bool) -> Self { + if dual_keys_supported { + Self::DUAL + } else { + Self::LEGACY + } + } + + #[cfg(not(feature = "future_snark"))] + fn select_from_support(_dual_keys_supported: bool) -> Self { + Self::LEGACY + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn version(version: &str) -> NodeVersion { + NodeVersion::new(semver::Version::parse(version).unwrap()) + } + + #[test] + fn legacy_keys_for_pythagoras_whatever_the_node_versions() { + let genesis_keys = + GenesisKeys::select(["pythagoras"], &version("0.10.1"), &version("0.13.23")); + + assert_eq!(GenesisKeys::LEGACY, genesis_keys); + } + + #[test] + fn legacy_keys_for_lagrange_on_an_aggregator_not_accepting_dual_keys() { + let genesis_keys = + GenesisKeys::select(["lagrange"], &version("0.9.5"), &version("0.13.23")); + + assert_eq!(GenesisKeys::LEGACY, genesis_keys); + } + + #[test] + fn legacy_keys_for_lagrange_on_a_client_not_accepting_dual_keys() { + let genesis_keys = + GenesisKeys::select(["lagrange"], &version("0.10.1"), &version("0.13.14")); + + assert_eq!(GenesisKeys::LEGACY, genesis_keys); + } + + #[cfg(feature = "future_snark")] + #[test] + fn dual_keys_for_lagrange_on_nodes_accepting_them() { + let genesis_keys = + GenesisKeys::select(["lagrange"], &version("0.9.6"), &version("0.13.15")); + + assert_eq!(GenesisKeys::DUAL, genesis_keys); + } + + #[cfg(feature = "future_snark")] + #[test] + fn dual_keys_when_lagrange_is_the_next_era() { + let genesis_keys = GenesisKeys::select( + ["pythagoras", "lagrange"], + &version("0.10.1"), + &version("0.13.23"), + ); + + assert_eq!(GenesisKeys::DUAL, genesis_keys); + } + + #[cfg(not(feature = "future_snark"))] + #[test] + fn legacy_keys_for_lagrange_without_the_snark_feature() { + let genesis_keys = + GenesisKeys::select(["lagrange"], &version("0.10.1"), &version("0.13.23")); + + assert_eq!(GenesisKeys::LEGACY, genesis_keys); + } +} 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 3f3f81d7030..58eea185e1d 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 @@ -18,8 +18,8 @@ use crate::mithril::relay_signer::RelaySignerConfiguration; use crate::toolkit::ScenarioToolkit; use crate::{ AggregateSignatureType, Aggregator, AggregatorConfig, Client, DEVNET_MAGIC_ID, Devnet, - DmqNodeFlavor, FullNode, IpfsDevnet, KuboNode, PoolNode, RelayAggregator, RelayPassive, - RelaySigner, Signer, + DmqNodeFlavor, FullNode, GenesisKeys, IpfsDevnet, KuboNode, PoolNode, RelayAggregator, + RelayPassive, RelaySigner, Signer, }; use super::signer::SignerConfig; @@ -52,6 +52,7 @@ pub struct MithrilInfrastructureConfig { pub startup_protocol_configuration: ProtocolConfiguration, pub signed_entity_types: Vec, pub aggregate_signature_type: AggregateSignatureType, + pub genesis_keys: GenesisKeys, pub use_relays: bool, pub relay_signer_registration_mode: String, pub relay_signature_registration_mode: String, @@ -102,6 +103,7 @@ impl MithrilInfrastructureConfig { }, signed_entity_types: vec!["type1".to_string()], aggregate_signature_type: AggregateSignatureType::Concatenation, + genesis_keys: GenesisKeys::LEGACY, use_relays: false, relay_signer_registration_mode: "passthrough".to_string(), relay_signature_registration_mode: "passthrough".to_string(), @@ -132,6 +134,7 @@ pub struct MithrilInfrastructure { era_reader_adapter: String, use_era_specific_work_dir: bool, aggregate_signature_type: AggregateSignatureType, + genesis_keys: GenesisKeys, } impl MithrilInfrastructure { @@ -236,6 +239,7 @@ impl MithrilInfrastructure { era_reader_adapter: config.mithril_era_reader_adapter.clone(), use_era_specific_work_dir: config.use_era_specific_work_dir, aggregate_signature_type: config.aggregate_signature_type, + genesis_keys: config.genesis_keys, }) } @@ -369,6 +373,7 @@ impl MithrilInfrastructure { aggregate_signature_type: config.aggregate_signature_type, chain_observer_type, leader_aggregator_endpoint: &leader_aggregator_endpoint, + genesis_keys: config.genesis_keys, use_dmq: config.use_dmq, dmq_node_flavor: &config.dmq_node_flavor, })?; @@ -642,7 +647,12 @@ impl MithrilInfrastructure { artifacts_dir }; - Client::new(aggregator.endpoint(), &work_dir, &self.bin_dir) + Client::new( + aggregator.endpoint(), + &work_dir, + &self.bin_dir, + self.genesis_keys, + ) } pub async fn tail_logs(&self, number_of_line: u64) -> StdResult<()> { diff --git a/mithril-test-lab/mithril-end-to-end/src/mithril/mod.rs b/mithril-test-lab/mithril-end-to-end/src/mithril/mod.rs index 5e99400f12b..bae555991a1 100644 --- a/mithril-test-lab/mithril-end-to-end/src/mithril/mod.rs +++ b/mithril-test-lab/mithril-end-to-end/src/mithril/mod.rs @@ -1,5 +1,6 @@ mod aggregator; mod client; +mod genesis_keys; mod infrastructure; mod relay_aggregator; mod relay_passive; @@ -12,6 +13,7 @@ pub use client::{ CardanoTransactionCommand, CardanoTransactionV2Command, Client, ClientCommand, MithrilStakeDistributionCommand, ToolsCommand, UtxoHdCommand, }; +pub use genesis_keys::GenesisKeys; pub use infrastructure::{ MithrilInfrastructure, MithrilInfrastructureConfig, ProtocolConfiguration, }; @@ -23,8 +25,6 @@ pub use signer::Signer; pub const DEVNET_MAGIC_ID: mithril_common::MagicId = 42; pub const DEVNET_DMQ_MAGIC_ID: mithril_common::MagicId = 2147483690; -pub const GENESIS_VERIFICATION_KEY: &str = "5b33322c3235332c3138362c3230312c3137372c31312c3131372c3133352c3138372c3136372c3138312c3138382c32322c35392c3230362c3130352c3233312c3135302c3231352c33302c37382c3231322c37362c31362c3235322c3138302c37322c3133342c3133372c3234372c3136312c36385d"; -pub const GENESIS_SECRET_KEY: &str = "5b3131382c3138342c3232342c3137332c3136302c3234312c36312c3134342c36342c39332c3130362c3232392c38332c3133342c3138392c34302c3138392c3231302c32352c3138342c3136302c3134312c3233372c32362c3136382c35342c3233392c3230342c3133392c3131392c31332c3139395d"; pub const ERA_MARKERS_VERIFICATION_KEY: &str = "5b33322c3235332c3138362c3230312c3137372c31312c3131372c3133352c3138372c3136372c3138312c3138382c32322c35392c3230362c3130352c3233312c3135302c3231352c33302c37382c3231322c37362c31362c3235322c3138302c37322c3133342c3133372c3234372c3136312c36385d"; pub const ERA_MARKERS_SECRET_KEY: &str = "5b3131382c3138342c3232342c3137332c3136302c3234312c36312c3134342c36342c39332c3130362c3232392c38332c3133342c3138392c34302c3138392c3231302c32352c3138342c3136302c3134312c3233372c32362c3136382c35342c3233392c3230342c3133392c3131392c31332c3139395d"; pub const ANCILLARY_MANIFEST_VERIFICATION_KEY: &str = "5b33322c3235332c3138362c3230312c3137372c31312c3131372c3133352c3138372c3136372c3138312c3138382c32322c35392c3230362c3130352c3233312c3135302c3231352c33302c37382c3231322c37362c31362c3235322c3138302c37322c3133342c3133372c3234372c3136312c36385d"; 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 c307ffb729c..538e719356b 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 @@ -11,7 +11,7 @@ use mithril_common::{ }; use crate::{ - AggregateSignatureType, Aggregator, AggregatorConfig, + AggregateSignatureType, Aggregator, AggregatorConfig, GenesisKeys, stress_test::{entities::AggregatorParameters, fake_chain, fake_signer, wait}, }; @@ -48,6 +48,7 @@ pub async fn bootstrap_aggregator( aggregate_signature_type: AggregateSignatureType::Concatenation, chain_observer_type, leader_aggregator_endpoint: &None, + genesis_keys: GenesisKeys::LEGACY, use_dmq: false, dmq_node_flavor: &None, })