use std::{
fmt,
fmt::{Display, Formatter},
};
use log::*;
use primitive_types::U512;
use serde::{Deserialize, Serialize};
use tari_common_types::types::{CompressedCommitment, HashOutput, PrivateKey};
use tari_mmr::{ArrayLike, pruned_hashset::PrunedHashSet};
use tari_node_components::blocks::{BlockError, BlockHeaderAccumulatedData};
use tari_transaction_components::{consensus::ConsensusConstants, tari_proof_of_work::PowAlgorithm};
use crate::proof_of_work::AchievedTargetDifficulty;
const LOG_TARGET: &str = "c::bn::acc_data";
#[derive(Default, Debug, Serialize, Deserialize)]
pub struct BlockAccumulatedData {
pub(crate) kernels: PrunedHashSet,
pub(crate) kernel_sum: CompressedCommitment,
}
impl BlockAccumulatedData {
pub fn new(kernels: PrunedHashSet, total_kernel_sum: CompressedCommitment) -> Self {
Self {
kernels,
kernel_sum: total_kernel_sum,
}
}
pub fn dissolve(self) -> PrunedHashSet {
self.kernels
}
pub fn kernel_sum(&self) -> &CompressedCommitment {
&self.kernel_sum
}
}
impl Display for BlockAccumulatedData {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{} hashes in kernel MMR,", self.kernels.len().unwrap_or(0))
}
}
#[derive(Debug, Clone, Default)]
pub struct UpdateBlockAccumulatedData {
pub kernel_hash_set: Option<PrunedHashSet>,
pub kernel_sum: Option<CompressedCommitment>,
}
pub struct BlockHeaderAccumulatedDataBuilder<'a> {
previous_accum: &'a BlockHeaderAccumulatedData,
hash: Option<HashOutput>,
current_total_kernel_offset: Option<PrivateKey>,
current_achieved_target: Option<AchievedTargetDifficulty>,
}
impl<'a> BlockHeaderAccumulatedDataBuilder<'a> {
pub fn from_previous(previous_accum: &'a BlockHeaderAccumulatedData) -> Self {
Self {
previous_accum,
hash: None,
current_total_kernel_offset: None,
current_achieved_target: None,
}
}
}
impl BlockHeaderAccumulatedDataBuilder<'_> {
pub fn with_hash(mut self, hash: HashOutput) -> Self {
self.hash = Some(hash);
self
}
pub fn with_total_kernel_offset(mut self, current_offset: PrivateKey) -> Self {
self.current_total_kernel_offset = Some(current_offset);
self
}
pub fn with_achieved_target_difficulty(mut self, achieved_target: AchievedTargetDifficulty) -> Self {
self.current_achieved_target = Some(achieved_target);
self
}
pub fn build(self, consensus_constants: &ConsensusConstants) -> Result<BlockHeaderAccumulatedData, BlockError> {
let previous_accum = self.previous_accum;
let hash = self.hash.ok_or(BlockError::BuilderMissingField { field: "hash" })?;
if hash == previous_accum.hash {
return Err(BlockError::BuilderInvalidValue {
field: "hash",
details: "Hash was set to the same hash that is contained in previous accumulated data".to_string(),
});
}
let achieved_target = self.current_achieved_target.ok_or(BlockError::BuilderMissingField {
field: "Current achieved difficulty",
})?;
let (monero_randomx_diff, tari_randomx_diff, sha3x_diff, cuckaroo_diff) = match achieved_target.pow_algo() {
PowAlgorithm::RandomXM => (
previous_accum
.accumulated_monero_randomx_difficulty
.checked_add_difficulty(achieved_target.target())
.ok_or(BlockError::DifficultyOverflow)?,
previous_accum.accumulated_tari_randomx_difficulty,
previous_accum.accumulated_sha3x_difficulty,
previous_accum.accumulated_cuckaroo_difficulty,
),
PowAlgorithm::RandomXT => (
previous_accum.accumulated_monero_randomx_difficulty,
previous_accum
.accumulated_tari_randomx_difficulty
.checked_add_difficulty(achieved_target.target())
.ok_or(BlockError::DifficultyOverflow)?,
previous_accum.accumulated_sha3x_difficulty,
previous_accum.accumulated_cuckaroo_difficulty,
),
PowAlgorithm::Sha3x => (
previous_accum.accumulated_monero_randomx_difficulty,
previous_accum.accumulated_tari_randomx_difficulty,
previous_accum
.accumulated_sha3x_difficulty
.checked_add_difficulty(achieved_target.target())
.ok_or(BlockError::DifficultyOverflow)?,
previous_accum.accumulated_cuckaroo_difficulty,
),
PowAlgorithm::Cuckaroo => (
previous_accum.accumulated_monero_randomx_difficulty,
previous_accum.accumulated_tari_randomx_difficulty,
previous_accum.accumulated_sha3x_difficulty,
previous_accum
.accumulated_cuckaroo_difficulty
.checked_add_difficulty(achieved_target.target())
.ok_or(BlockError::DifficultyOverflow)?,
),
};
let total_kernel_offset = self
.current_total_kernel_offset
.map(|offset| &previous_accum.total_kernel_offset + offset)
.ok_or(BlockError::BuilderMissingField {
field: "total_kernel_offset",
})?;
let mut total_accumulated: U512 = U512::from(monero_randomx_diff.as_u128()) *
U512::from(tari_randomx_diff.as_u128()) *
U512::from(sha3x_diff.as_u128());
if consensus_constants.include_c29_accumulated_difficulty_into_total() {
total_accumulated *= U512::from(cuckaroo_diff.as_u128());
}
let result = BlockHeaderAccumulatedData {
hash,
total_kernel_offset,
achieved_difficulty: achieved_target.achieved(),
total_accumulated_difficulty: total_accumulated,
accumulated_monero_randomx_difficulty: monero_randomx_diff,
accumulated_tari_randomx_difficulty: tari_randomx_diff,
accumulated_sha3x_difficulty: sha3x_diff,
accumulated_cuckaroo_difficulty: cuckaroo_diff,
target_difficulty: achieved_target.target(),
};
trace!(
target: LOG_TARGET,
"Calculated: Tot_acc_diff {}, Monero RandomX {}, Tari RandomX {}, SHA3 {}, Cuckaroo {}",
result.total_accumulated_difficulty,
result.accumulated_monero_randomx_difficulty,
result.accumulated_tari_randomx_difficulty,
result.accumulated_sha3x_difficulty,
result.accumulated_cuckaroo_difficulty,
);
Ok(result)
}
}