diff --git a/base_layer/core/src/proof_of_work/monero_rx/error.rs b/base_layer/core/src/proof_of_work/monero_rx/error.rs index 97bcb60507..1038b6ab8e 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/error.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/error.rs @@ -28,6 +28,8 @@ use crate::proof_of_work::DifficultyError; /// Errors that can occur when merging Monero PoW data with Tari PoW data #[derive(Debug, thiserror::Error)] pub enum MergeMineError { + #[error("Serialized POWData does not match provided data: {0}")] + SerializedPowDataDoesNotMatch(String), #[error("Serialization error: {0}")] SerializeError(String), #[error("Error deserializing Monero data: {0}")] diff --git a/base_layer/core/src/proof_of_work/monero_rx/merkle_tree.rs b/base_layer/core/src/proof_of_work/monero_rx/merkle_tree.rs index 9ca181398b..1ea67d86ab 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/merkle_tree.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/merkle_tree.rs @@ -173,6 +173,9 @@ impl MerkleProof { } /// Calculates the merkle root hash from the provide Monero hash + /// The coinbase must be the first transaction in the block, so + /// that you can't have multiple coinbases in a block. That means the coinbase + /// is always the leftmost branch in the merkle tree pub fn calculate_root(&self, hash: &Hash) -> Hash { if self.branch.is_empty() { return *hash; diff --git a/base_layer/core/src/proof_of_work/monero_rx/pow_data.rs b/base_layer/core/src/proof_of_work/monero_rx/pow_data.rs index 5b05b61cb3..e5cd075903 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/pow_data.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/pow_data.rs @@ -103,6 +103,21 @@ impl MoneroPowData { v.len() ))); } + let mut test_serialized_data = vec![]; + + // This is an inefficient test, so maybe it can be removed in future, but because we rely + // on third party parsing libraries, there could be a case where the data we deserialized + // can be generated from multiple input data. This way we test that there is only one of those + // inputs that is allowed. Remember that the data in powdata is used for the hash, so having + // multiple pow_data that generate the same randomx difficulty could be a problem. + BorshSerialize::serialize(&pow_data, &mut test_serialized_data) + .map_err(|e| MergeMineError::SerializeError(format!("{:?}", e)))?; + if test_serialized_data != tari_header.pow.pow_data { + return Err(MergeMineError::SerializedPowDataDoesNotMatch( + "Serialized pow data does not match original pow data".to_string(), + )); + } + Ok(pow_data) }