use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::{collections::BTreeMap, fmt::Display};
use crate::protocol::ToMessage;
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum ProtocolMessagePartKey {
#[serde(rename = "snapshot_digest")]
SnapshotDigest,
#[serde(rename = "cardano_transactions_merkle_root")]
CardanoTransactionsMerkleRoot,
#[serde(rename = "next_aggregate_verification_key")]
NextAggregateVerificationKey,
#[serde(rename = "next_protocol_parameters")]
NextProtocolParameters,
#[serde(rename = "current_epoch")]
CurrentEpoch,
#[serde(rename = "latest_block_number")]
LatestBlockNumber,
#[serde(rename = "cardano_stake_distribution_epoch")]
CardanoStakeDistributionEpoch,
#[serde(rename = "cardano_stake_distribution_merkle_root")]
CardanoStakeDistributionMerkleRoot,
#[serde(rename = "cardano_database_merkle_root")]
CardanoDatabaseMerkleRoot,
}
impl Display for ProtocolMessagePartKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Self::SnapshotDigest => write!(f, "snapshot_digest"),
Self::NextAggregateVerificationKey => write!(f, "next_aggregate_verification_key"),
Self::NextProtocolParameters => write!(f, "next_protocol_parameters"),
Self::CurrentEpoch => write!(f, "current_epoch"),
Self::CardanoTransactionsMerkleRoot => write!(f, "cardano_transactions_merkle_root"),
Self::LatestBlockNumber => write!(f, "latest_block_number"),
Self::CardanoStakeDistributionEpoch => write!(f, "cardano_stake_distribution_epoch"),
Self::CardanoStakeDistributionMerkleRoot => {
write!(f, "cardano_stake_distribution_merkle_root")
}
Self::CardanoDatabaseMerkleRoot => write!(f, "cardano_database_merkle_root"),
}
}
}
pub type ProtocolMessagePartValue = String;
#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
pub struct ProtocolMessage {
pub message_parts: BTreeMap<ProtocolMessagePartKey, ProtocolMessagePartValue>,
}
impl ProtocolMessage {
pub fn new() -> ProtocolMessage {
ProtocolMessage {
message_parts: BTreeMap::new(),
}
}
pub fn set_message_part(
&mut self,
key: ProtocolMessagePartKey,
value: ProtocolMessagePartValue,
) -> Option<ProtocolMessagePartValue> {
self.message_parts.insert(key, value)
}
pub fn get_message_part(
&self,
key: &ProtocolMessagePartKey,
) -> Option<&ProtocolMessagePartValue> {
self.message_parts.get(key)
}
pub fn compute_hash(&self) -> String {
let mut hasher = Sha256::new();
self.message_parts.iter().for_each(|(k, v)| {
hasher.update(k.to_string().as_bytes());
hasher.update(v.as_bytes());
});
hex::encode(hasher.finalize())
}
}
impl ToMessage for ProtocolMessage {
fn to_message(&self) -> String {
self.compute_hash()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_protocol_message_compute_hash_include_next_aggregate_verification_key() {
let protocol_message = build_protocol_message_reference();
let hash_expected = protocol_message.compute_hash();
let mut protocol_message_modified = protocol_message.clone();
protocol_message_modified.set_message_part(
ProtocolMessagePartKey::NextAggregateVerificationKey,
"next-avk-456".to_string(),
);
assert_ne!(hash_expected, protocol_message_modified.compute_hash());
}
#[test]
fn test_protocol_message_compute_hash_include_snapshot_digest() {
let protocol_message = build_protocol_message_reference();
let hash_expected = protocol_message.compute_hash();
let mut protocol_message_modified = protocol_message.clone();
protocol_message_modified.set_message_part(
ProtocolMessagePartKey::SnapshotDigest,
"snapshot-digest-456".to_string(),
);
assert_ne!(hash_expected, protocol_message_modified.compute_hash());
}
#[test]
fn test_protocol_message_compute_hash_include_cardano_transactions_merkle_root() {
let protocol_message = build_protocol_message_reference();
let hash_expected = protocol_message.compute_hash();
let mut protocol_message_modified = protocol_message.clone();
protocol_message_modified.set_message_part(
ProtocolMessagePartKey::CardanoTransactionsMerkleRoot,
"ctx-merke-root-456".to_string(),
);
assert_ne!(hash_expected, protocol_message_modified.compute_hash());
}
#[test]
fn test_protocol_message_compute_hash_include_cardano_stake_distribution_epoch() {
let protocol_message = build_protocol_message_reference();
let hash_expected = protocol_message.compute_hash();
let mut protocol_message_modified = protocol_message.clone();
protocol_message_modified.set_message_part(
ProtocolMessagePartKey::CardanoStakeDistributionEpoch,
"cardano-stake-distribution-epoch-456".to_string(),
);
assert_ne!(hash_expected, protocol_message_modified.compute_hash());
}
#[test]
fn test_protocol_message_compute_hash_include_cardano_stake_distribution_merkle_root() {
let protocol_message = build_protocol_message_reference();
let hash_expected = protocol_message.compute_hash();
let mut protocol_message_modified = protocol_message.clone();
protocol_message_modified.set_message_part(
ProtocolMessagePartKey::CardanoStakeDistributionMerkleRoot,
"cardano-stake-distribution-merkle-root-456".to_string(),
);
assert_ne!(hash_expected, protocol_message_modified.compute_hash());
}
#[test]
fn test_protocol_message_compute_hash_include_lastest_immutable_file_number() {
let protocol_message = build_protocol_message_reference();
let hash_expected = protocol_message.compute_hash();
let mut protocol_message_modified = protocol_message.clone();
protocol_message_modified.set_message_part(
ProtocolMessagePartKey::LatestBlockNumber,
"latest-immutable-file-number-456".to_string(),
);
assert_ne!(hash_expected, protocol_message_modified.compute_hash());
}
#[test]
fn test_protocol_message_compute_hash_include_cardano_database_merkle_root() {
let protocol_message = build_protocol_message_reference();
let hash_expected = protocol_message.compute_hash();
let mut protocol_message_modified = protocol_message.clone();
protocol_message_modified.set_message_part(
ProtocolMessagePartKey::CardanoDatabaseMerkleRoot,
"cardano-database-merkle-root-456".to_string(),
);
assert_ne!(hash_expected, protocol_message_modified.compute_hash());
}
#[test]
fn test_protocol_message_compute_hash_include_next_protocol_parameters() {
let protocol_message = build_protocol_message_reference();
let hash_expected = protocol_message.compute_hash();
let mut protocol_message_modified = protocol_message.clone();
protocol_message_modified.set_message_part(
ProtocolMessagePartKey::NextProtocolParameters,
"latest-protocol-parameters-456".to_string(),
);
assert_ne!(hash_expected, protocol_message_modified.compute_hash());
}
#[test]
fn test_protocol_message_compute_hash_the_same_hash_with_same_protocol_message() {
assert_eq!(
build_protocol_message_reference().compute_hash(),
build_protocol_message_reference().compute_hash()
);
}
fn build_protocol_message_reference() -> ProtocolMessage {
let mut protocol_message = ProtocolMessage::new();
protocol_message.set_message_part(
ProtocolMessagePartKey::SnapshotDigest,
"snapshot-digest-123".to_string(),
);
protocol_message.set_message_part(
ProtocolMessagePartKey::NextAggregateVerificationKey,
"next-avk-123".to_string(),
);
protocol_message.set_message_part(
ProtocolMessagePartKey::NextProtocolParameters,
"next-protocol-parameters-123".to_string(),
);
protocol_message.set_message_part(
ProtocolMessagePartKey::CardanoTransactionsMerkleRoot,
"ctx-merkle-root-123".to_string(),
);
protocol_message.set_message_part(
ProtocolMessagePartKey::LatestBlockNumber,
"latest-immutable-file-number-123".to_string(),
);
protocol_message.set_message_part(
ProtocolMessagePartKey::CardanoStakeDistributionEpoch,
"cardano-stake-distribution-epoch-123".to_string(),
);
protocol_message.set_message_part(
ProtocolMessagePartKey::CardanoStakeDistributionMerkleRoot,
"cardano-stake-distribution-merkle-root-123".to_string(),
);
protocol_message.set_message_part(
ProtocolMessagePartKey::CardanoDatabaseMerkleRoot,
"cardano-database-merkle-root-123".to_string(),
);
protocol_message
}
}