use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
path::{Path, PathBuf},
};
#[cfg(feature = "future_snark")]
use crate::crypto_helper::{
ProtocolKey, ProtocolSignerVerificationKeyForSnark,
ProtocolSignerVerificationKeySignatureForSnark,
};
use crate::{
StdResult,
certificate_chain::CertificateGenesisProducer,
crypto_helper::{
GenesisSigner, ProtocolAggregateVerificationKey,
ProtocolAggregateVerificationKeyForConcatenation, ProtocolClosedKeyRegistration,
ProtocolInitializer, ProtocolOpCert, ProtocolSigner,
ProtocolSignerVerificationKeyForConcatenation,
ProtocolSignerVerificationKeySignatureForConcatenation, ProtocolStakeDistribution,
},
entities::{
Certificate, CertificateSignature, Epoch, HexEncodedAggregateVerificationKey, PartyId,
ProtocolParameters, Signer, SignerWithStake, SingleSignature, Stake, StakeDistribution,
StakeDistributionParty, SupportedEra,
},
protocol::{SignerBuilder, ToMessage},
test::crypto_helper::ProtocolInitializerTestExtension,
};
#[derive(Debug, Clone)]
pub struct MithrilFixture {
protocol_parameters: ProtocolParameters,
signers: Vec<SignerFixture>,
stake_distribution: ProtocolStakeDistribution,
}
#[derive(Debug, Clone)]
pub struct SignerFixture {
pub signer_with_stake: SignerWithStake,
pub protocol_signer: ProtocolSigner,
pub protocol_initializer: ProtocolInitializer,
pub protocol_closed_key_registration: ProtocolClosedKeyRegistration,
pub kes_secret_key_path: Option<PathBuf>,
pub operational_certificate_path: Option<PathBuf>,
}
impl SignerFixture {
pub fn try_new_with_protocol_parameters(
self,
protocol_parameters: ProtocolParameters,
) -> StdResult<Self> {
let mut protocol_initializer = self.protocol_initializer.clone();
protocol_initializer.override_protocol_parameters(&protocol_parameters.into());
let protocol_signer =
protocol_initializer.new_signer(self.protocol_closed_key_registration.clone())?;
Ok(Self {
protocol_signer,
..self
})
}
}
impl From<SignerFixture> for SignerWithStake {
fn from(fixture: SignerFixture) -> Self {
fixture.signer_with_stake
}
}
impl From<&SignerFixture> for SignerWithStake {
fn from(fixture: &SignerFixture) -> Self {
fixture.signer_with_stake.clone()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CardanoCliStakeDistribution {
#[serde(rename = "pools")]
pub signers: HashMap<String, CardanoCliSignerStake>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CardanoCliSignerStake {
#[serde(rename = "stakeMark")]
actual_stake: Stake,
#[serde(rename = "stakeSet")]
previous_stake: Stake,
#[serde(rename = "stakeGo")]
penultimate_stake: Stake,
}
impl MithrilFixture {
pub fn new(
protocol_parameters: ProtocolParameters,
signers: Vec<SignerFixture>,
stake_distribution: ProtocolStakeDistribution,
) -> Self {
Self {
protocol_parameters,
signers,
stake_distribution,
}
}
pub fn protocol_parameters(&self) -> ProtocolParameters {
self.protocol_parameters.clone()
}
pub fn signers_fixture(&self) -> Vec<SignerFixture> {
self.signers.clone()
}
pub fn signers(&self) -> Vec<Signer> {
self.signers
.clone()
.into_iter()
.map(|s| s.signer_with_stake.into())
.collect()
}
pub fn signers_with_stake(&self) -> Vec<SignerWithStake> {
self.signers.iter().map(|s| &s.signer_with_stake).cloned().collect()
}
pub fn stake_distribution_parties(&self) -> Vec<StakeDistributionParty> {
self.signers
.iter()
.map(|s| StakeDistributionParty {
party_id: s.signer_with_stake.party_id.clone(),
stake: s.signer_with_stake.stake,
})
.collect()
}
pub fn stake_distribution(&self) -> StakeDistribution {
StakeDistribution::from_iter(self.stake_distribution.clone())
}
pub fn protocol_stake_distribution(&self) -> ProtocolStakeDistribution {
self.stake_distribution.clone()
}
pub fn cardano_cli_stake_distribution(&self) -> CardanoCliStakeDistribution {
let signers = HashMap::from_iter(self.signers_fixture().into_iter().map(|signer| {
(
signer.compute_protocol_party_id_as_hash(),
CardanoCliSignerStake {
actual_stake: signer.signer_with_stake.stake,
previous_stake: signer.signer_with_stake.stake,
penultimate_stake: signer.signer_with_stake.stake,
},
)
}));
CardanoCliStakeDistribution { signers }
}
pub fn compute_aggregate_verification_key(&self) -> ProtocolAggregateVerificationKey {
SignerBuilder::new(&self.signers_with_stake(), &self.protocol_parameters)
.unwrap()
.compute_aggregate_verification_key()
}
pub fn compute_concatenation_aggregate_verification_key(
&self,
) -> ProtocolAggregateVerificationKeyForConcatenation {
self.compute_aggregate_verification_key()
.to_concatenation_aggregate_verification_key()
.to_owned()
.into()
}
pub fn compute_and_encode_concatenation_aggregate_verification_key(
&self,
) -> HexEncodedAggregateVerificationKey {
let aggregate_verification_key = self.compute_concatenation_aggregate_verification_key();
aggregate_verification_key.to_json_hex().unwrap()
}
#[cfg(feature = "future_snark")]
pub fn compute_snark_aggregate_verification_key(
&self,
) -> Option<crate::crypto_helper::ProtocolAggregateVerificationKeyForSnark> {
self.compute_aggregate_verification_key()
.to_snark_aggregate_verification_key()
.map(|key| ProtocolKey::new(key.to_owned()))
}
#[cfg(feature = "future_snark")]
pub fn compute_and_encode_snark_aggregate_verification_key(&self) -> Option<String> {
self.compute_snark_aggregate_verification_key()
.map(|avk| avk.to_bytes_hex().unwrap())
}
pub fn create_genesis_certificate<T: Into<String>>(
&self,
network: T,
epoch: Epoch,
) -> Certificate {
let genesis_avk = self.compute_aggregate_verification_key();
let genesis_signer = GenesisSigner::create_deterministic_signer();
let genesis_producer = CertificateGenesisProducer::new();
let mithril_era = SupportedEra::Pythagoras;
let genesis_protocol_message = genesis_producer
.create_genesis_protocol_message(
&self.protocol_parameters,
&genesis_avk,
&epoch,
mithril_era,
)
.unwrap();
let signature = genesis_signer
.sign_deterministic(&genesis_protocol_message, mithril_era)
.unwrap();
match signature {
CertificateSignature::GenesisSignature(genesis_signature) => genesis_producer
.create_legacy_genesis_certificate(
self.protocol_parameters.clone(),
network,
epoch,
genesis_avk,
genesis_signature,
mithril_era,
),
#[cfg(feature = "future_snark")]
CertificateSignature::GenesisDualSignature(
genesis_signature,
genesis_signature_snark,
) => genesis_producer.create_genesis_certificate(
self.protocol_parameters.clone(),
network,
epoch,
genesis_avk,
genesis_signature,
genesis_signature_snark,
mithril_era,
),
CertificateSignature::MultiSignature(..) => {
unreachable!("the genesis signer never produces a multi-signature")
}
}
.unwrap()
}
pub fn sign_all<T: ToMessage>(&self, message: &T) -> Vec<SingleSignature> {
self.signers.par_iter().filter_map(|s| s.sign(message)).collect()
}
}
impl From<MithrilFixture> for Vec<Signer> {
fn from(fixture: MithrilFixture) -> Self {
fixture.signers()
}
}
impl From<MithrilFixture> for Vec<SignerWithStake> {
fn from(fixture: MithrilFixture) -> Self {
fixture.signers_with_stake()
}
}
impl From<MithrilFixture> for Vec<SignerFixture> {
fn from(fixture: MithrilFixture) -> Self {
fixture.signers_fixture()
}
}
impl SignerFixture {
pub fn sign<T: ToMessage>(&self, message: &T) -> Option<SingleSignature> {
let message = message.to_message();
self.protocol_signer.sign(message.as_bytes()).map(|signature| {
let won_indexes = signature.get_concatenation_signature_indices();
SingleSignature::new(
self.signer_with_stake.party_id.to_owned(),
signature.into(),
won_indexes,
)
})
}
pub fn party_id(&self) -> PartyId {
self.signer_with_stake.party_id.clone()
}
pub fn operational_certificate(&self) -> Option<ProtocolOpCert> {
self.signer_with_stake.operational_certificate.clone()
}
pub fn compute_protocol_party_id_as_hash(&self) -> String {
self.operational_certificate()
.unwrap()
.compute_protocol_party_id_as_hash()
}
pub fn verification_key_for_concatenation(
&self,
) -> ProtocolSignerVerificationKeyForConcatenation {
self.signer_with_stake.verification_key_for_concatenation
}
pub fn verification_key_signature_for_concatenation(
&self,
) -> Option<ProtocolSignerVerificationKeySignatureForConcatenation> {
self.signer_with_stake.verification_key_signature_for_concatenation
}
#[cfg(feature = "future_snark")]
pub fn verification_key_for_snark(&self) -> Option<ProtocolSignerVerificationKeyForSnark> {
self.signer_with_stake.verification_key_for_snark
}
#[cfg(feature = "future_snark")]
pub fn verification_key_signature_for_snark(
&self,
) -> Option<ProtocolSignerVerificationKeySignatureForSnark> {
self.signer_with_stake.verification_key_signature_for_snark
}
pub fn kes_secret_key_path(&self) -> Option<&Path> {
self.kes_secret_key_path.as_deref()
}
pub fn operational_certificate_path(&self) -> Option<&Path> {
self.operational_certificate_path.as_deref()
}
}