From cdc3eafe99a2f8db2290052972bb40ed6a7c7ce1 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Wed, 22 Nov 2023 09:09:29 +0200 Subject: [PATCH 01/14] wip --- .../src/proof_of_work/monero_rx/helpers.rs | 1302 +++++++++-------- .../proof_of_work/monero_rx/merkle_tree.rs | 161 +- .../monero_rx/merkle_tree_parameters.rs | 90 ++ .../core/src/proof_of_work/monero_rx/mod.rs | 2 + .../src/proof_of_work/monero_rx/pow_data.rs | 11 +- .../core/tests/tests/block_validation.rs | 2 +- 6 files changed, 880 insertions(+), 688 deletions(-) create mode 100644 base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs diff --git a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs index e3b130a864..876e50f285 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs @@ -29,6 +29,7 @@ use monero::{ VarInt, }; use tari_utilities::hex::HexError; +use tari_common_types::types::FixedHash; use super::{ error::MergeMineError, @@ -107,9 +108,10 @@ pub fn verify_header(header: &BlockHeader) -> Result Result bool{ + // let merkle_parameters = merkle_tree_params. +true +} + /// Extracts the Monero block hash from the coinbase transaction's extra field pub fn extract_tari_hash_from_block(monero: &monero::Block) -> Result, MergeMineError> { // When we extract the merge mining hash, we do not care if the extra field can be parsed without error. @@ -176,7 +183,7 @@ pub fn serialize_monero_block_to_hex(obj: &monero::Block) -> Result Result { let hashes = create_ordered_transaction_hashes_from_block(&block); let root = tree_hash(&hashes)?; - let coinbase_merkle_proof = create_merkle_proof(&hashes).ok_or_else(|| { + let coinbase_merkle_proof = create_merkle_proof(&hashes, &hashes[0]).ok_or_else(|| { MergeMineError::ValidationError( "create_merkle_proof returned None because the block had no coinbase (which is impossible because the \ Block type does not allow that)" @@ -189,8 +196,9 @@ pub fn construct_monero_data(block: monero::Block, seed: FixedByteArray) -> Resu randomx_key: seed, transaction_count: hashes.len() as u16, merkle_root: root, - coinbase_merkle_proof, + coinbase_merkle_proof: coinbase_merkle_proof.clone(), coinbase_tx: block.miner_tx, + aux_chain_merkle_proof: coinbase_merkle_proof }) } @@ -258,644 +266,644 @@ pub fn create_block_hashing_blob( blockhashing_blob } -#[cfg(test)] -mod test { - use std::convert::{TryFrom, TryInto}; - - use borsh::BorshSerialize; - use monero::{ - blockdata::transaction::{ExtraField, TxOutTarget}, - consensus::deserialize, - cryptonote::hash::Hashable, - util::ringct::{RctSig, RctSigBase, RctType}, - Hash, - PublicKey, - Transaction, - TransactionPrefix, - TxIn, - TxOut, - }; - use tari_common_types::types::FixedHash; - use tari_test_utils::unpack_enum; - use tari_utilities::{ - epoch_time::EpochTime, - hex::{from_hex, Hex}, - ByteArray, - }; - - use super::*; - use crate::proof_of_work::{monero_rx::fixed_array::FixedByteArray, PowAlgorithm, ProofOfWork}; - - // This tests checks the hash of monero-rs - #[test] - fn test_monero_rs_miner_tx_hash() { - let tx = "f8ad7c58e6fce1792dd78d764ce88a11db0e3c3bb484d868ae05a7321fb6c6b0"; - - let pk_extra = vec![ - 179, 155, 220, 223, 213, 23, 81, 160, 95, 232, 87, 102, 151, 63, 70, 249, 139, 40, 110, 16, 51, 193, 175, - 208, 38, 120, 65, 191, 155, 139, 1, 4, - ]; - let transaction = Transaction { - prefix: TransactionPrefix { - version: VarInt(2), - unlock_time: VarInt(2143845), - inputs: vec![TxIn::Gen { - height: VarInt(2143785), - }], - outputs: vec![TxOut { - amount: VarInt(1550800739964), - target: TxOutTarget::ToKey { - key: hex::decode("e2e19d8badb15e77c8e1f441cf6acd9bcde34a07cae82bbe5ff9629bf88e6e81") - .unwrap() - .as_slice() - .try_into() - .unwrap(), - }, - }], - extra: ExtraField(vec![ - SubField::TxPublicKey(PublicKey::from_slice(pk_extra.as_slice()).unwrap()), - SubField::Nonce(vec![196, 37, 4, 0, 27, 37, 187, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - ]) - .into(), - }, - signatures: vec![], - rct_signatures: RctSig { - sig: Option::from(RctSigBase { - rct_type: RctType::Null, - txn_fee: Default::default(), - pseudo_outs: vec![], - ecdh_info: vec![], - out_pk: vec![], - }), - p: None, - }, - }; - assert_eq!( - tx.as_bytes().to_vec(), - hex::encode(transaction.hash().0.to_vec()).as_bytes().to_vec() - ); - let hex = hex::encode(consensus::serialize::(&transaction)); - deserialize::(&hex::decode(hex).unwrap()).unwrap(); - } - - // This tests checks the blockhashing blob of monero-rs - #[test] - fn test_monero_rs_block_serialize() { - // block with only the miner tx and no other transactions - let hex = "0c0c94debaf805beb3489c722a285c092a32e7c6893abfc7d069699c8326fc3445a749c5276b6200000000029b892201ffdf882201b699d4c8b1ec020223df524af2a2ef5f870adb6e1ceb03a475c39f8b9ef76aa50b46ddd2a18349402b012839bfa19b7524ec7488917714c216ca254b38ed0424ca65ae828a7c006aeaf10208f5316a7f6b99cca60000"; - // blockhashing blob for above block as accepted by monero - let hex_blockhash_blob="0c0c94debaf805beb3489c722a285c092a32e7c6893abfc7d069699c8326fc3445a749c5276b6200000000602d0d4710e2c2d38da0cce097accdf5dc18b1d34323880c1aae90ab8f6be6e201"; - let bytes = hex::decode(hex).unwrap(); - let block = deserialize::(&bytes[..]).unwrap(); - let header = consensus::serialize::(&block.header); - let tx_count = 1 + block.tx_hashes.len() as u64; - let mut count = consensus::serialize::(&VarInt(tx_count)); - #[allow(clippy::cast_possible_truncation)] - let mut hashes = Vec::with_capacity(tx_count as usize); - hashes.push(block.miner_tx.hash()); - for item in block.clone().tx_hashes { - hashes.push(item); - } - let root = tree_hash(&hashes).unwrap(); - let mut encode2 = header; - encode2.extend_from_slice(root.as_bytes()); - encode2.append(&mut count); - assert_eq!(hex::encode(encode2), hex_blockhash_blob); - let bytes2 = consensus::serialize::(&block); - assert_eq!(bytes, bytes2); - let hex2 = hex::encode(bytes2); - assert_eq!(hex, hex2); - } - - #[test] - fn test_monero_data() { - let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); - let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); - let bytes = hex::decode(blocktemplate_blob).unwrap(); - let mut block = deserialize::(&bytes[..]).unwrap(); - let mut block_header = BlockHeader { - version: 0, - height: 0, - prev_hash: FixedHash::zero(), - timestamp: EpochTime::now(), - output_mr: FixedHash::zero(), - output_smt_size: 0, - kernel_mr: FixedHash::zero(), - kernel_mmr_size: 0, - input_mr: FixedHash::zero(), - total_kernel_offset: Default::default(), - total_script_offset: Default::default(), - nonce: 0, - pow: ProofOfWork::default(), - validator_node_mr: FixedHash::zero(), - validator_node_size: 0, - }; - let hash = block_header.merge_mining_hash(); - insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); - let hashes = create_ordered_transaction_hashes_from_block(&block); - assert_eq!(hashes.len(), block.tx_hashes.len() + 1); - let root = tree_hash(&hashes).unwrap(); - let coinbase_merkle_proof = create_merkle_proof(&hashes).unwrap(); - - let monero_data = MoneroPowData { - header: block.header, - randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), - transaction_count: u16::try_from(hashes.len()).unwrap(), - merkle_root: root, - coinbase_merkle_proof, - coinbase_tx: block.miner_tx, - }; - let mut serialized = Vec::new(); - monero_data.serialize(&mut serialized).unwrap(); - let pow = ProofOfWork { - pow_algo: PowAlgorithm::RandomX, - pow_data: serialized, - }; - block_header.pow = pow; - MoneroPowData::from_header(&block_header).unwrap(); - } - - #[test] - fn test_input_blob() { - let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); - let bytes = hex::decode(blocktemplate_blob).unwrap(); - let block = deserialize::(&bytes[..]).unwrap(); - let input_blob = create_blockhashing_blob_from_block(&block).unwrap(); - assert_eq!(input_blob, "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000058b030b6800d433bbcb2b560afe2a08e4dc152fa77ead96d37aaf14897d3c09601"); - } - - #[test] - fn test_append_mm_tag() { - let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); - let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); - let bytes = hex::decode(blocktemplate_blob).unwrap(); - let mut block = deserialize::(&bytes[..]).unwrap(); - let mut block_header = BlockHeader { - version: 0, - height: 0, - prev_hash: FixedHash::zero(), - timestamp: EpochTime::now(), - output_mr: FixedHash::zero(), - output_smt_size: 0, - kernel_mr: FixedHash::zero(), - kernel_mmr_size: 0, - input_mr: FixedHash::zero(), - total_kernel_offset: Default::default(), - total_script_offset: Default::default(), - nonce: 0, - pow: ProofOfWork::default(), - validator_node_mr: FixedHash::zero(), - validator_node_size: 0, - }; - let hash = block_header.merge_mining_hash(); - insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); - let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); - let mut hashes = Vec::with_capacity(count as usize); - hashes.push(block.miner_tx.hash()); - // Note: tx_hashes is empty, so |hashes| == 1 - for item in block.clone().tx_hashes { - hashes.push(item); - } - let root = tree_hash(&hashes).unwrap(); - assert_eq!(root, hashes[0]); - let coinbase_merkle_proof = create_merkle_proof(&hashes).unwrap(); - let monero_data = MoneroPowData { - header: block.header, - randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), - transaction_count: count, - merkle_root: root, - coinbase_merkle_proof, - coinbase_tx: block.miner_tx, - }; - let mut serialized = Vec::new(); - monero_data.serialize(&mut serialized).unwrap(); - let pow = ProofOfWork { - pow_algo: PowAlgorithm::RandomX, - pow_data: serialized, - }; - block_header.pow = pow; - verify_header(&block_header).unwrap(); - } - - #[test] - fn test_append_mm_tag_no_tag() { - let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); - let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); - let bytes = hex::decode(blocktemplate_blob).unwrap(); - let block = deserialize::(&bytes[..]).unwrap(); - let mut block_header = BlockHeader { - version: 0, - height: 0, - prev_hash: FixedHash::zero(), - timestamp: EpochTime::now(), - output_mr: FixedHash::zero(), - output_smt_size: 0, - kernel_mr: FixedHash::zero(), - kernel_mmr_size: 0, - input_mr: FixedHash::zero(), - total_kernel_offset: Default::default(), - total_script_offset: Default::default(), - nonce: 0, - pow: ProofOfWork::default(), - validator_node_mr: FixedHash::zero(), - validator_node_size: 0, - }; - let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); - let mut hashes = Vec::with_capacity(count as usize); - hashes.push(block.miner_tx.hash()); - for item in block.clone().tx_hashes { - hashes.push(item); - } - let root = tree_hash(&hashes).unwrap(); - let coinbase_merkle_proof = create_merkle_proof(&hashes).unwrap(); - let monero_data = MoneroPowData { - header: block.header, - randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), - transaction_count: count, - merkle_root: root, - coinbase_merkle_proof, - coinbase_tx: block.miner_tx, - }; - - let mut serialized = Vec::new(); - monero_data.serialize(&mut serialized).unwrap(); - let pow = ProofOfWork { - pow_algo: PowAlgorithm::RandomX, - pow_data: serialized, - }; - block_header.pow = pow; - let err = verify_header(&block_header).unwrap_err(); - unpack_enum!(MergeMineError::ValidationError(details) = err); - assert!(details.contains("Expected merge mining tag was not found in Monero coinbase transaction")); - } - - #[test] - fn test_append_mm_tag_wrong_hash() { - let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); - let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); - let bytes = hex::decode(blocktemplate_blob).unwrap(); - let mut block = deserialize::(&bytes[..]).unwrap(); - let mut block_header = BlockHeader { - version: 0, - height: 0, - prev_hash: FixedHash::zero(), - timestamp: EpochTime::now(), - output_mr: FixedHash::zero(), - output_smt_size: 0, - kernel_mr: FixedHash::zero(), - kernel_mmr_size: 0, - input_mr: FixedHash::zero(), - total_kernel_offset: Default::default(), - total_script_offset: Default::default(), - nonce: 0, - pow: ProofOfWork::default(), - validator_node_mr: FixedHash::zero(), - validator_node_size: 0, - }; - let hash = Hash::null(); - insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); - let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); - let mut hashes = Vec::with_capacity(count as usize); - let mut proof = Vec::with_capacity(count as usize); - hashes.push(block.miner_tx.hash()); - proof.push(block.miner_tx.hash()); - for item in block.clone().tx_hashes { - hashes.push(item); - proof.push(item); - } - let root = tree_hash(&hashes).unwrap(); - let coinbase_merkle_proof = create_merkle_proof(&hashes).unwrap(); - let monero_data = MoneroPowData { - header: block.header, - randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), - transaction_count: count, - merkle_root: root, - coinbase_merkle_proof, - coinbase_tx: block.miner_tx, - }; - let mut serialized = Vec::new(); - monero_data.serialize(&mut serialized).unwrap(); - let pow = ProofOfWork { - pow_algo: PowAlgorithm::RandomX, - pow_data: serialized, - }; - block_header.pow = pow; - let err = verify_header(&block_header).unwrap_err(); - unpack_enum!(MergeMineError::ValidationError(details) = err); - assert!(details.contains("Expected merge mining tag was not found in Monero coinbase transaction")); - } - - #[test] - fn test_duplicate_append_mm_tag() { - let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); - let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); - let bytes = hex::decode(blocktemplate_blob).unwrap(); - let mut block = deserialize::(&bytes[..]).unwrap(); - let mut block_header = BlockHeader { - version: 0, - height: 0, - prev_hash: FixedHash::zero(), - timestamp: EpochTime::now(), - output_mr: FixedHash::zero(), - output_smt_size: 0, - kernel_mr: FixedHash::zero(), - kernel_mmr_size: 0, - input_mr: FixedHash::zero(), - total_kernel_offset: Default::default(), - total_script_offset: Default::default(), - nonce: 0, - pow: ProofOfWork::default(), - validator_node_mr: FixedHash::zero(), - validator_node_size: 0, - }; - let hash = block_header.merge_mining_hash(); - insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); - #[allow(clippy::redundant_clone)] - let mut block_header2 = block_header.clone(); - block_header2.version = 1; - let hash2 = block_header2.merge_mining_hash(); - assert!(extract_tari_hash_from_block(&block).is_ok()); - - // Try via the API - this will fail because more than one merge mining tag is not allowed - assert!(insert_merge_mining_tag_into_block(&mut block, hash2).is_err()); - - // Now bypass the API - this will effectively allow us to insert more than one merge mining tag, - // like trying to sneek it in. Later on, when we call `verify_header(&block_header)`, it should fail. - let mut extra_field = ExtraField::try_parse(&block.miner_tx.prefix.extra).unwrap(); - let hash = monero::Hash::from_slice(hash.as_ref()); - extra_field.0.insert(0, SubField::MergeMining(Some(VarInt(0)), hash)); - block.miner_tx.prefix.extra = extra_field.into(); - - // Trying to extract the Tari hash will fail because there are more than one merge mining tag - let err = extract_tari_hash_from_block(&block).unwrap_err(); - unpack_enum!(MergeMineError::ValidationError(details) = err); - assert!(details.contains("More than one merge mining tag found in coinbase")); - - let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); - let mut hashes = Vec::with_capacity(count as usize); - hashes.push(block.miner_tx.hash()); - // Note: tx_hashes is empty, so |hashes| == 1 - for item in block.clone().tx_hashes { - hashes.push(item); - } - let root = tree_hash(&hashes).unwrap(); - assert_eq!(root, hashes[0]); - let coinbase_merkle_proof = create_merkle_proof(&hashes).unwrap(); - let monero_data = MoneroPowData { - header: block.header, - randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), - transaction_count: count, - merkle_root: root, - coinbase_merkle_proof, - coinbase_tx: block.miner_tx, - }; - let mut serialized = Vec::new(); - monero_data.serialize(&mut serialized).unwrap(); - let pow = ProofOfWork { - pow_algo: PowAlgorithm::RandomX, - pow_data: serialized, - }; - block_header.pow = pow; - - // Header verification will fail because there are more than one merge mining tag - let err = verify_header(&block_header).unwrap_err(); - unpack_enum!(MergeMineError::ValidationError(details) = err); - assert!(details.contains("More than one merge mining tag found in coinbase")); - } - - #[test] - fn test_extra_field_with_parsing_error() { - let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); - let bytes = hex::decode(blocktemplate_blob).unwrap(); - let mut block = deserialize::(&bytes[..]).unwrap(); - let block_header = BlockHeader { - version: 0, - height: 0, - prev_hash: FixedHash::zero(), - timestamp: EpochTime::now(), - output_mr: FixedHash::zero(), - output_smt_size: 0, - kernel_mr: FixedHash::zero(), - kernel_mmr_size: 0, - input_mr: FixedHash::zero(), - total_kernel_offset: Default::default(), - total_script_offset: Default::default(), - nonce: 0, - pow: ProofOfWork::default(), - validator_node_mr: FixedHash::zero(), - validator_node_size: 2, - }; - - // Let us manipulate the extra field to make it invalid - let mut extra_field_before_parse = ExtraField::try_parse(&block.miner_tx.prefix.extra).unwrap(); - assert_eq!( - "ExtraField([TxPublicKey(06225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa4782), Nonce([246, \ - 58, 168, 109, 46, 133, 127, 7])])", - &format!("{:?}", extra_field_before_parse) - ); - assert!(ExtraField::try_parse(&extra_field_before_parse.clone().into()).is_ok()); - - extra_field_before_parse.0.insert(0, SubField::Padding(230)); - assert_eq!( - "ExtraField([Padding(230), TxPublicKey(06225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa4782), \ - Nonce([246, 58, 168, 109, 46, 133, 127, 7])])", - &format!("{:?}", extra_field_before_parse) - ); - assert!(ExtraField::try_parse(&extra_field_before_parse.clone().into()).is_err()); - - // Now insert the merge mining tag - this would also clean up the extra field and remove the invalid sub-fields - let hash = block_header.merge_mining_hash(); - insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); - assert!(ExtraField::try_parse(&block.miner_tx.prefix.extra.clone()).is_ok()); - - // Verify that the merge mining tag is there - let extra_field_after_tag = ExtraField::try_parse(&block.miner_tx.prefix.extra.clone()).unwrap(); - assert_eq!( - &format!( - "ExtraField([MergeMining(Some(0), 0x{}), \ - TxPublicKey(06225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa4782), Nonce([246, 58, 168, \ - 109, 46, 133, 127, 7])])", - hex::encode(hash) - ), - &format!("{:?}", extra_field_after_tag) - ); - } - - #[test] - fn test_verify_header_no_coinbase() { - let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); - let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); - let bytes = hex::decode(blocktemplate_blob).unwrap(); - let mut block = deserialize::(&bytes[..]).unwrap(); - let mut block_header = BlockHeader { - version: 0, - height: 0, - prev_hash: FixedHash::zero(), - timestamp: EpochTime::now(), - output_mr: FixedHash::zero(), - output_smt_size: 0, - kernel_mr: FixedHash::zero(), - kernel_mmr_size: 0, - input_mr: FixedHash::zero(), - total_kernel_offset: Default::default(), - total_script_offset: Default::default(), - nonce: 0, - pow: ProofOfWork::default(), - validator_node_mr: FixedHash::zero(), - validator_node_size: 0, - }; - let hash = block_header.merge_mining_hash(); - insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); - let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); - let mut hashes = Vec::with_capacity(count as usize); - let mut proof = Vec::with_capacity(count as usize); - hashes.push(block.miner_tx.hash()); - proof.push(block.miner_tx.hash()); - for item in block.clone().tx_hashes { - hashes.push(item); - proof.push(item); - } - let root = tree_hash(&hashes).unwrap(); - let coinbase_merkle_proof = create_merkle_proof(&hashes).unwrap(); - let monero_data = MoneroPowData { - header: block.header, - randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), - transaction_count: count, - merkle_root: root, - coinbase_merkle_proof, - coinbase_tx: Default::default(), - }; - let mut serialized = Vec::new(); - monero_data.serialize(&mut serialized).unwrap(); - let pow = ProofOfWork { - pow_algo: PowAlgorithm::RandomX, - pow_data: serialized, - }; - block_header.pow = pow; - let err = verify_header(&block_header).unwrap_err(); - unpack_enum!(MergeMineError::ValidationError(details) = err); - assert!(details.contains("Expected merge mining tag was not found in Monero coinbase transaction")); - } - - #[test] - fn test_verify_header_no_data() { - let mut block_header = BlockHeader { - version: 0, - height: 0, - prev_hash: FixedHash::zero(), - timestamp: EpochTime::now(), - output_mr: FixedHash::zero(), - output_smt_size: 0, - kernel_mr: FixedHash::zero(), - kernel_mmr_size: 0, - input_mr: FixedHash::zero(), - total_kernel_offset: Default::default(), - total_script_offset: Default::default(), - nonce: 0, - pow: ProofOfWork::default(), - validator_node_mr: FixedHash::zero(), - validator_node_size: 0, - }; - let monero_data = MoneroPowData { - header: Default::default(), - randomx_key: FixedByteArray::default(), - transaction_count: 1, - merkle_root: Default::default(), - coinbase_merkle_proof: create_merkle_proof(&[Hash::null()]).unwrap(), - coinbase_tx: Default::default(), - }; - let mut serialized = Vec::new(); - monero_data.serialize(&mut serialized).unwrap(); - let pow = ProofOfWork { - pow_algo: PowAlgorithm::RandomX, - pow_data: serialized, - }; - block_header.pow = pow; - let err = verify_header(&block_header).unwrap_err(); - unpack_enum!(MergeMineError::ValidationError(details) = err); - assert!(details.contains("Expected merge mining tag was not found in Monero coinbase transaction")); - } - - #[test] - fn test_verify_invalid_root() { - let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); - let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); - let bytes = hex::decode(blocktemplate_blob).unwrap(); - let mut block = deserialize::(&bytes[..]).unwrap(); - let mut block_header = BlockHeader { - version: 0, - height: 0, - prev_hash: FixedHash::zero(), - timestamp: EpochTime::now(), - output_mr: FixedHash::zero(), - output_smt_size: 0, - kernel_mr: FixedHash::zero(), - kernel_mmr_size: 0, - input_mr: FixedHash::zero(), - total_kernel_offset: Default::default(), - total_script_offset: Default::default(), - nonce: 0, - pow: ProofOfWork::default(), - validator_node_mr: FixedHash::zero(), - validator_node_size: 0, - }; - let hash = block_header.merge_mining_hash(); - insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); - let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); - let mut hashes = Vec::with_capacity(count as usize); - let mut proof = Vec::with_capacity(count as usize); - hashes.push(block.miner_tx.hash()); - proof.push(block.miner_tx.hash()); - for item in block.clone().tx_hashes { - hashes.push(item); - proof.push(item); - } - - let coinbase_merkle_proof = create_merkle_proof(&hashes).unwrap(); - let monero_data = MoneroPowData { - header: block.header, - randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), - transaction_count: count, - merkle_root: Hash::null(), - coinbase_merkle_proof, - coinbase_tx: block.miner_tx, - }; - let mut serialized = Vec::new(); - monero_data.serialize(&mut serialized).unwrap(); - let pow = ProofOfWork { - pow_algo: PowAlgorithm::RandomX, - pow_data: serialized, - }; - block_header.pow = pow; - let err = verify_header(&block_header).unwrap_err(); - unpack_enum!(MergeMineError::InvalidMerkleRoot = err); - } - - #[test] - fn test_difficulty() { - // Taken from block: https://stagenet.xmrchain.net/search?value=672576 - let versions = "0c0c"; - // Tool for encoding VarInts: - // https://gchq.github.io/CyberChef/#recipe=VarInt_Encode()To_Hex('Space',0)From_Hex('Auto'/disabled)VarInt_Decode(/disabled)&input=MTYwMTAzMTIwMg - let timestamp = "a298b7fb05"; // 1601031202 - let prev_block = "046f4fe371f9acdc27c377f4adee84e93b11f89246a74dd77f1bf0856141da5c"; - let nonce = "FE394F12"; // 307182078 - let tx_hash = "77139305ea53cfe95cf7235d2fed6fca477395b019b98060acdbc0f8fb0b8b92"; // miner tx - let count = "01"; - - let input = from_hex(&format!( - "{}{}{}{}{}{}", - versions, timestamp, prev_block, nonce, tx_hash, count - )) - .unwrap(); - let key = from_hex("2aca6501719a5c7ab7d4acbc7cc5d277b57ad8c27c6830788c2d5a596308e5b1").unwrap(); - let rx = RandomXFactory::default(); - - let (difficulty, hash) = get_random_x_difficulty(&input, &rx.create(&key).unwrap()).unwrap(); - assert_eq!( - hash.to_hex(), - "f68fbc8cc85bde856cd1323e9f8e6f024483038d728835de2f8c014ff6260000" - ); - assert_eq!(difficulty.as_u64(), 430603); - } -} +// #[cfg(test)] +// mod test { +// use std::convert::{TryFrom, TryInto}; +// +// use borsh::BorshSerialize; +// use monero::{ +// blockdata::transaction::{ExtraField, TxOutTarget}, +// consensus::deserialize, +// cryptonote::hash::Hashable, +// util::ringct::{RctSig, RctSigBase, RctType}, +// Hash, +// PublicKey, +// Transaction, +// TransactionPrefix, +// TxIn, +// TxOut, +// }; +// use tari_common_types::types::FixedHash; +// use tari_test_utils::unpack_enum; +// use tari_utilities::{ +// epoch_time::EpochTime, +// hex::{from_hex, Hex}, +// ByteArray, +// }; +// +// use super::*; +// use crate::proof_of_work::{monero_rx::fixed_array::FixedByteArray, PowAlgorithm, ProofOfWork}; +// +// // This tests checks the hash of monero-rs +// #[test] +// fn test_monero_rs_miner_tx_hash() { +// let tx = "f8ad7c58e6fce1792dd78d764ce88a11db0e3c3bb484d868ae05a7321fb6c6b0"; +// +// let pk_extra = vec![ +// 179, 155, 220, 223, 213, 23, 81, 160, 95, 232, 87, 102, 151, 63, 70, 249, 139, 40, 110, 16, 51, 193, 175, +// 208, 38, 120, 65, 191, 155, 139, 1, 4, +// ]; +// let transaction = Transaction { +// prefix: TransactionPrefix { +// version: VarInt(2), +// unlock_time: VarInt(2143845), +// inputs: vec![TxIn::Gen { +// height: VarInt(2143785), +// }], +// outputs: vec![TxOut { +// amount: VarInt(1550800739964), +// target: TxOutTarget::ToKey { +// key: hex::decode("e2e19d8badb15e77c8e1f441cf6acd9bcde34a07cae82bbe5ff9629bf88e6e81") +// .unwrap() +// .as_slice() +// .try_into() +// .unwrap(), +// }, +// }], +// extra: ExtraField(vec![ +// SubField::TxPublicKey(PublicKey::from_slice(pk_extra.as_slice()).unwrap()), +// SubField::Nonce(vec![196, 37, 4, 0, 27, 37, 187, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0]), +// ]) +// .into(), +// }, +// signatures: vec![], +// rct_signatures: RctSig { +// sig: Option::from(RctSigBase { +// rct_type: RctType::Null, +// txn_fee: Default::default(), +// pseudo_outs: vec![], +// ecdh_info: vec![], +// out_pk: vec![], +// }), +// p: None, +// }, +// }; +// assert_eq!( +// tx.as_bytes().to_vec(), +// hex::encode(transaction.hash().0.to_vec()).as_bytes().to_vec() +// ); +// let hex = hex::encode(consensus::serialize::(&transaction)); +// deserialize::(&hex::decode(hex).unwrap()).unwrap(); +// } +// +// // This tests checks the blockhashing blob of monero-rs +// #[test] +// fn test_monero_rs_block_serialize() { +// // block with only the miner tx and no other transactions +// let hex = "0c0c94debaf805beb3489c722a285c092a32e7c6893abfc7d069699c8326fc3445a749c5276b6200000000029b892201ffdf882201b699d4c8b1ec020223df524af2a2ef5f870adb6e1ceb03a475c39f8b9ef76aa50b46ddd2a18349402b012839bfa19b7524ec7488917714c216ca254b38ed0424ca65ae828a7c006aeaf10208f5316a7f6b99cca60000"; +// // blockhashing blob for above block as accepted by monero +// let hex_blockhash_blob="0c0c94debaf805beb3489c722a285c092a32e7c6893abfc7d069699c8326fc3445a749c5276b6200000000602d0d4710e2c2d38da0cce097accdf5dc18b1d34323880c1aae90ab8f6be6e201"; +// let bytes = hex::decode(hex).unwrap(); +// let block = deserialize::(&bytes[..]).unwrap(); +// let header = consensus::serialize::(&block.header); +// let tx_count = 1 + block.tx_hashes.len() as u64; +// let mut count = consensus::serialize::(&VarInt(tx_count)); +// #[allow(clippy::cast_possible_truncation)] +// let mut hashes = Vec::with_capacity(tx_count as usize); +// hashes.push(block.miner_tx.hash()); +// for item in block.clone().tx_hashes { +// hashes.push(item); +// } +// let root = tree_hash(&hashes).unwrap(); +// let mut encode2 = header; +// encode2.extend_from_slice(root.as_bytes()); +// encode2.append(&mut count); +// assert_eq!(hex::encode(encode2), hex_blockhash_blob); +// let bytes2 = consensus::serialize::(&block); +// assert_eq!(bytes, bytes2); +// let hex2 = hex::encode(bytes2); +// assert_eq!(hex, hex2); +// } +// +// #[test] +// fn test_monero_data() { +// let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); +// let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); +// let bytes = hex::decode(blocktemplate_blob).unwrap(); +// let mut block = deserialize::(&bytes[..]).unwrap(); +// let mut block_header = BlockHeader { +// version: 0, +// height: 0, +// prev_hash: FixedHash::zero(), +// timestamp: EpochTime::now(), +// output_mr: FixedHash::zero(), +// output_smt_size: 0, +// kernel_mr: FixedHash::zero(), +// kernel_mmr_size: 0, +// input_mr: FixedHash::zero(), +// total_kernel_offset: Default::default(), +// total_script_offset: Default::default(), +// nonce: 0, +// pow: ProofOfWork::default(), +// validator_node_mr: FixedHash::zero(), +// validator_node_size: 0, +// }; +// let hash = block_header.merge_mining_hash(); +// insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); +// let hashes = create_ordered_transaction_hashes_from_block(&block); +// assert_eq!(hashes.len(), block.tx_hashes.len() + 1); +// let root = tree_hash(&hashes).unwrap(); +// let coinbase_merkle_proof = create_merkle_proof(&hashes, &hashes[0]).unwrap(); +// +// let monero_data = MoneroPowData { +// header: block.header, +// randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), +// transaction_count: u16::try_from(hashes.len()).unwrap(), +// merkle_root: root, +// coinbase_merkle_proof, +// coinbase_tx: block.miner_tx, +// }; +// let mut serialized = Vec::new(); +// monero_data.serialize(&mut serialized).unwrap(); +// let pow = ProofOfWork { +// pow_algo: PowAlgorithm::RandomX, +// pow_data: serialized, +// }; +// block_header.pow = pow; +// MoneroPowData::from_header(&block_header).unwrap(); +// } +// +// #[test] +// fn test_input_blob() { +// let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); +// let bytes = hex::decode(blocktemplate_blob).unwrap(); +// let block = deserialize::(&bytes[..]).unwrap(); +// let input_blob = create_blockhashing_blob_from_block(&block).unwrap(); +// assert_eq!(input_blob, "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000058b030b6800d433bbcb2b560afe2a08e4dc152fa77ead96d37aaf14897d3c09601"); +// } +// +// #[test] +// fn test_append_mm_tag() { +// let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); +// let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); +// let bytes = hex::decode(blocktemplate_blob).unwrap(); +// let mut block = deserialize::(&bytes[..]).unwrap(); +// let mut block_header = BlockHeader { +// version: 0, +// height: 0, +// prev_hash: FixedHash::zero(), +// timestamp: EpochTime::now(), +// output_mr: FixedHash::zero(), +// output_smt_size: 0, +// kernel_mr: FixedHash::zero(), +// kernel_mmr_size: 0, +// input_mr: FixedHash::zero(), +// total_kernel_offset: Default::default(), +// total_script_offset: Default::default(), +// nonce: 0, +// pow: ProofOfWork::default(), +// validator_node_mr: FixedHash::zero(), +// validator_node_size: 0, +// }; +// let hash = block_header.merge_mining_hash(); +// insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); +// let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); +// let mut hashes = Vec::with_capacity(count as usize); +// hashes.push(block.miner_tx.hash()); +// // Note: tx_hashes is empty, so |hashes| == 1 +// for item in block.clone().tx_hashes { +// hashes.push(item); +// } +// let root = tree_hash(&hashes).unwrap(); +// assert_eq!(root, hashes[0]); +// let coinbase_merkle_proof = create_merkle_proof(&hashes,&hashes[0]).unwrap(); +// let monero_data = MoneroPowData { +// header: block.header, +// randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), +// transaction_count: count, +// merkle_root: root, +// coinbase_merkle_proof, +// coinbase_tx: block.miner_tx, +// }; +// let mut serialized = Vec::new(); +// monero_data.serialize(&mut serialized).unwrap(); +// let pow = ProofOfWork { +// pow_algo: PowAlgorithm::RandomX, +// pow_data: serialized, +// }; +// block_header.pow = pow; +// verify_header(&block_header).unwrap(); +// } +// +// #[test] +// fn test_append_mm_tag_no_tag() { +// let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); +// let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); +// let bytes = hex::decode(blocktemplate_blob).unwrap(); +// let block = deserialize::(&bytes[..]).unwrap(); +// let mut block_header = BlockHeader { +// version: 0, +// height: 0, +// prev_hash: FixedHash::zero(), +// timestamp: EpochTime::now(), +// output_mr: FixedHash::zero(), +// output_smt_size: 0, +// kernel_mr: FixedHash::zero(), +// kernel_mmr_size: 0, +// input_mr: FixedHash::zero(), +// total_kernel_offset: Default::default(), +// total_script_offset: Default::default(), +// nonce: 0, +// pow: ProofOfWork::default(), +// validator_node_mr: FixedHash::zero(), +// validator_node_size: 0, +// }; +// let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); +// let mut hashes = Vec::with_capacity(count as usize); +// hashes.push(block.miner_tx.hash()); +// for item in block.clone().tx_hashes { +// hashes.push(item); +// } +// let root = tree_hash(&hashes).unwrap(); +// let coinbase_merkle_proof = create_merkle_proof(&hashes,&hashes[0]).unwrap(); +// let monero_data = MoneroPowData { +// header: block.header, +// randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), +// transaction_count: count, +// merkle_root: root, +// coinbase_merkle_proof, +// coinbase_tx: block.miner_tx, +// }; +// +// let mut serialized = Vec::new(); +// monero_data.serialize(&mut serialized).unwrap(); +// let pow = ProofOfWork { +// pow_algo: PowAlgorithm::RandomX, +// pow_data: serialized, +// }; +// block_header.pow = pow; +// let err = verify_header(&block_header).unwrap_err(); +// unpack_enum!(MergeMineError::ValidationError(details) = err); +// assert!(details.contains("Expected merge mining tag was not found in Monero coinbase transaction")); +// } +// +// #[test] +// fn test_append_mm_tag_wrong_hash() { +// let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); +// let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); +// let bytes = hex::decode(blocktemplate_blob).unwrap(); +// let mut block = deserialize::(&bytes[..]).unwrap(); +// let mut block_header = BlockHeader { +// version: 0, +// height: 0, +// prev_hash: FixedHash::zero(), +// timestamp: EpochTime::now(), +// output_mr: FixedHash::zero(), +// output_smt_size: 0, +// kernel_mr: FixedHash::zero(), +// kernel_mmr_size: 0, +// input_mr: FixedHash::zero(), +// total_kernel_offset: Default::default(), +// total_script_offset: Default::default(), +// nonce: 0, +// pow: ProofOfWork::default(), +// validator_node_mr: FixedHash::zero(), +// validator_node_size: 0, +// }; +// let hash = Hash::null(); +// insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); +// let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); +// let mut hashes = Vec::with_capacity(count as usize); +// let mut proof = Vec::with_capacity(count as usize); +// hashes.push(block.miner_tx.hash()); +// proof.push(block.miner_tx.hash()); +// for item in block.clone().tx_hashes { +// hashes.push(item); +// proof.push(item); +// } +// let root = tree_hash(&hashes).unwrap(); +// let coinbase_merkle_proof = create_merkle_proof(&hashes, &hashes[0]).unwrap(); +// let monero_data = MoneroPowData { +// header: block.header, +// randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), +// transaction_count: count, +// merkle_root: root, +// coinbase_merkle_proof, +// coinbase_tx: block.miner_tx, +// }; +// let mut serialized = Vec::new(); +// monero_data.serialize(&mut serialized).unwrap(); +// let pow = ProofOfWork { +// pow_algo: PowAlgorithm::RandomX, +// pow_data: serialized, +// }; +// block_header.pow = pow; +// let err = verify_header(&block_header).unwrap_err(); +// unpack_enum!(MergeMineError::ValidationError(details) = err); +// assert!(details.contains("Expected merge mining tag was not found in Monero coinbase transaction")); +// } +// +// #[test] +// fn test_duplicate_append_mm_tag() { +// let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); +// let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); +// let bytes = hex::decode(blocktemplate_blob).unwrap(); +// let mut block = deserialize::(&bytes[..]).unwrap(); +// let mut block_header = BlockHeader { +// version: 0, +// height: 0, +// prev_hash: FixedHash::zero(), +// timestamp: EpochTime::now(), +// output_mr: FixedHash::zero(), +// output_smt_size: 0, +// kernel_mr: FixedHash::zero(), +// kernel_mmr_size: 0, +// input_mr: FixedHash::zero(), +// total_kernel_offset: Default::default(), +// total_script_offset: Default::default(), +// nonce: 0, +// pow: ProofOfWork::default(), +// validator_node_mr: FixedHash::zero(), +// validator_node_size: 0, +// }; +// let hash = block_header.merge_mining_hash(); +// insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); +// #[allow(clippy::redundant_clone)] +// let mut block_header2 = block_header.clone(); +// block_header2.version = 1; +// let hash2 = block_header2.merge_mining_hash(); +// assert!(extract_tari_hash_from_block(&block).is_ok()); +// +// // Try via the API - this will fail because more than one merge mining tag is not allowed +// assert!(insert_merge_mining_tag_into_block(&mut block, hash2).is_err()); +// +// // Now bypass the API - this will effectively allow us to insert more than one merge mining tag, +// // like trying to sneek it in. Later on, when we call `verify_header(&block_header)`, it should fail. +// let mut extra_field = ExtraField::try_parse(&block.miner_tx.prefix.extra).unwrap(); +// let hash = monero::Hash::from_slice(hash.as_ref()); +// extra_field.0.insert(0, SubField::MergeMining(Some(VarInt(0)), hash)); +// block.miner_tx.prefix.extra = extra_field.into(); +// +// // Trying to extract the Tari hash will fail because there are more than one merge mining tag +// let err = extract_tari_hash_from_block(&block).unwrap_err(); +// unpack_enum!(MergeMineError::ValidationError(details) = err); +// assert!(details.contains("More than one merge mining tag found in coinbase")); +// +// let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); +// let mut hashes = Vec::with_capacity(count as usize); +// hashes.push(block.miner_tx.hash()); +// // Note: tx_hashes is empty, so |hashes| == 1 +// for item in block.clone().tx_hashes { +// hashes.push(item); +// } +// let root = tree_hash(&hashes).unwrap(); +// assert_eq!(root, hashes[0]); +// let coinbase_merkle_proof = create_merkle_proof(&hashes, &hashes[0]).unwrap(); +// let monero_data = MoneroPowData { +// header: block.header, +// randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), +// transaction_count: count, +// merkle_root: root, +// coinbase_merkle_proof, +// coinbase_tx: block.miner_tx, +// }; +// let mut serialized = Vec::new(); +// monero_data.serialize(&mut serialized).unwrap(); +// let pow = ProofOfWork { +// pow_algo: PowAlgorithm::RandomX, +// pow_data: serialized, +// }; +// block_header.pow = pow; +// +// // Header verification will fail because there are more than one merge mining tag +// let err = verify_header(&block_header).unwrap_err(); +// unpack_enum!(MergeMineError::ValidationError(details) = err); +// assert!(details.contains("More than one merge mining tag found in coinbase")); +// } +// +// #[test] +// fn test_extra_field_with_parsing_error() { +// let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); +// let bytes = hex::decode(blocktemplate_blob).unwrap(); +// let mut block = deserialize::(&bytes[..]).unwrap(); +// let block_header = BlockHeader { +// version: 0, +// height: 0, +// prev_hash: FixedHash::zero(), +// timestamp: EpochTime::now(), +// output_mr: FixedHash::zero(), +// output_smt_size: 0, +// kernel_mr: FixedHash::zero(), +// kernel_mmr_size: 0, +// input_mr: FixedHash::zero(), +// total_kernel_offset: Default::default(), +// total_script_offset: Default::default(), +// nonce: 0, +// pow: ProofOfWork::default(), +// validator_node_mr: FixedHash::zero(), +// validator_node_size: 2, +// }; +// +// // Let us manipulate the extra field to make it invalid +// let mut extra_field_before_parse = ExtraField::try_parse(&block.miner_tx.prefix.extra).unwrap(); +// assert_eq!( +// "ExtraField([TxPublicKey(06225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa4782), Nonce([246, \ +// 58, 168, 109, 46, 133, 127, 7])])", +// &format!("{:?}", extra_field_before_parse) +// ); +// assert!(ExtraField::try_parse(&extra_field_before_parse.clone().into()).is_ok()); +// +// extra_field_before_parse.0.insert(0, SubField::Padding(230)); +// assert_eq!( +// "ExtraField([Padding(230), TxPublicKey(06225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa4782), \ +// Nonce([246, 58, 168, 109, 46, 133, 127, 7])])", +// &format!("{:?}", extra_field_before_parse) +// ); +// assert!(ExtraField::try_parse(&extra_field_before_parse.clone().into()).is_err()); +// +// // Now insert the merge mining tag - this would also clean up the extra field and remove the invalid sub-fields +// let hash = block_header.merge_mining_hash(); +// insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); +// assert!(ExtraField::try_parse(&block.miner_tx.prefix.extra.clone()).is_ok()); +// +// // Verify that the merge mining tag is there +// let extra_field_after_tag = ExtraField::try_parse(&block.miner_tx.prefix.extra.clone()).unwrap(); +// assert_eq!( +// &format!( +// "ExtraField([MergeMining(Some(0), 0x{}), \ +// TxPublicKey(06225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa4782), Nonce([246, 58, 168, \ +// 109, 46, 133, 127, 7])])", +// hex::encode(hash) +// ), +// &format!("{:?}", extra_field_after_tag) +// ); +// } +// +// #[test] +// fn test_verify_header_no_coinbase() { +// let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); +// let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); +// let bytes = hex::decode(blocktemplate_blob).unwrap(); +// let mut block = deserialize::(&bytes[..]).unwrap(); +// let mut block_header = BlockHeader { +// version: 0, +// height: 0, +// prev_hash: FixedHash::zero(), +// timestamp: EpochTime::now(), +// output_mr: FixedHash::zero(), +// output_smt_size: 0, +// kernel_mr: FixedHash::zero(), +// kernel_mmr_size: 0, +// input_mr: FixedHash::zero(), +// total_kernel_offset: Default::default(), +// total_script_offset: Default::default(), +// nonce: 0, +// pow: ProofOfWork::default(), +// validator_node_mr: FixedHash::zero(), +// validator_node_size: 0, +// }; +// let hash = block_header.merge_mining_hash(); +// insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); +// let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); +// let mut hashes = Vec::with_capacity(count as usize); +// let mut proof = Vec::with_capacity(count as usize); +// hashes.push(block.miner_tx.hash()); +// proof.push(block.miner_tx.hash()); +// for item in block.clone().tx_hashes { +// hashes.push(item); +// proof.push(item); +// } +// let root = tree_hash(&hashes).unwrap(); +// let coinbase_merkle_proof = create_merkle_proof(&hashes,&hashes[0]).unwrap(); +// let monero_data = MoneroPowData { +// header: block.header, +// randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), +// transaction_count: count, +// merkle_root: root, +// coinbase_merkle_proof, +// coinbase_tx: Default::default(), +// }; +// let mut serialized = Vec::new(); +// monero_data.serialize(&mut serialized).unwrap(); +// let pow = ProofOfWork { +// pow_algo: PowAlgorithm::RandomX, +// pow_data: serialized, +// }; +// block_header.pow = pow; +// let err = verify_header(&block_header).unwrap_err(); +// unpack_enum!(MergeMineError::ValidationError(details) = err); +// assert!(details.contains("Expected merge mining tag was not found in Monero coinbase transaction")); +// } +// +// #[test] +// fn test_verify_header_no_data() { +// let mut block_header = BlockHeader { +// version: 0, +// height: 0, +// prev_hash: FixedHash::zero(), +// timestamp: EpochTime::now(), +// output_mr: FixedHash::zero(), +// output_smt_size: 0, +// kernel_mr: FixedHash::zero(), +// kernel_mmr_size: 0, +// input_mr: FixedHash::zero(), +// total_kernel_offset: Default::default(), +// total_script_offset: Default::default(), +// nonce: 0, +// pow: ProofOfWork::default(), +// validator_node_mr: FixedHash::zero(), +// validator_node_size: 0, +// }; +// let monero_data = MoneroPowData { +// header: Default::default(), +// randomx_key: FixedByteArray::default(), +// transaction_count: 1, +// merkle_root: Default::default(), +// coinbase_merkle_proof: create_merkle_proof(&[Hash::null()], &Hash::null()).unwrap(), +// coinbase_tx: Default::default(), +// }; +// let mut serialized = Vec::new(); +// monero_data.serialize(&mut serialized).unwrap(); +// let pow = ProofOfWork { +// pow_algo: PowAlgorithm::RandomX, +// pow_data: serialized, +// }; +// block_header.pow = pow; +// let err = verify_header(&block_header).unwrap_err(); +// unpack_enum!(MergeMineError::ValidationError(details) = err); +// assert!(details.contains("Expected merge mining tag was not found in Monero coinbase transaction")); +// } +// +// #[test] +// fn test_verify_invalid_root() { +// let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); +// let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); +// let bytes = hex::decode(blocktemplate_blob).unwrap(); +// let mut block = deserialize::(&bytes[..]).unwrap(); +// let mut block_header = BlockHeader { +// version: 0, +// height: 0, +// prev_hash: FixedHash::zero(), +// timestamp: EpochTime::now(), +// output_mr: FixedHash::zero(), +// output_smt_size: 0, +// kernel_mr: FixedHash::zero(), +// kernel_mmr_size: 0, +// input_mr: FixedHash::zero(), +// total_kernel_offset: Default::default(), +// total_script_offset: Default::default(), +// nonce: 0, +// pow: ProofOfWork::default(), +// validator_node_mr: FixedHash::zero(), +// validator_node_size: 0, +// }; +// let hash = block_header.merge_mining_hash(); +// insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); +// let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); +// let mut hashes = Vec::with_capacity(count as usize); +// let mut proof = Vec::with_capacity(count as usize); +// hashes.push(block.miner_tx.hash()); +// proof.push(block.miner_tx.hash()); +// for item in block.clone().tx_hashes { +// hashes.push(item); +// proof.push(item); +// } +// +// let coinbase_merkle_proof = create_merkle_proof(&hashes, &hashes[0]).unwrap(); +// let monero_data = MoneroPowData { +// header: block.header, +// randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), +// transaction_count: count, +// merkle_root: Hash::null(), +// coinbase_merkle_proof, +// coinbase_tx: block.miner_tx, +// }; +// let mut serialized = Vec::new(); +// monero_data.serialize(&mut serialized).unwrap(); +// let pow = ProofOfWork { +// pow_algo: PowAlgorithm::RandomX, +// pow_data: serialized, +// }; +// block_header.pow = pow; +// let err = verify_header(&block_header).unwrap_err(); +// unpack_enum!(MergeMineError::InvalidMerkleRoot = err); +// } +// +// #[test] +// fn test_difficulty() { +// // Taken from block: https://stagenet.xmrchain.net/search?value=672576 +// let versions = "0c0c"; +// // Tool for encoding VarInts: +// // https://gchq.github.io/CyberChef/#recipe=VarInt_Encode()To_Hex('Space',0)From_Hex('Auto'/disabled)VarInt_Decode(/disabled)&input=MTYwMTAzMTIwMg +// let timestamp = "a298b7fb05"; // 1601031202 +// let prev_block = "046f4fe371f9acdc27c377f4adee84e93b11f89246a74dd77f1bf0856141da5c"; +// let nonce = "FE394F12"; // 307182078 +// let tx_hash = "77139305ea53cfe95cf7235d2fed6fca477395b019b98060acdbc0f8fb0b8b92"; // miner tx +// let count = "01"; +// +// let input = from_hex(&format!( +// "{}{}{}{}{}{}", +// versions, timestamp, prev_block, nonce, tx_hash, count +// )) +// .unwrap(); +// let key = from_hex("2aca6501719a5c7ab7d4acbc7cc5d277b57ad8c27c6830788c2d5a596308e5b1").unwrap(); +// let rx = RandomXFactory::default(); +// +// let (difficulty, hash) = get_random_x_difficulty(&input, &rx.create(&key).unwrap()).unwrap(); +// assert_eq!( +// hash.to_hex(), +// "f68fbc8cc85bde856cd1323e9f8e6f024483038d728835de2f8c014ff6260000" +// ); +// assert_eq!(difficulty.as_u64(), 430603); +// } +// } 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 1ea67d86ab..a51fb03e6f 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 @@ -128,6 +128,7 @@ pub fn tree_hash(hashes: &[Hash]) -> Result { #[cfg_attr(test, derive(PartialEq))] pub struct MerkleProof { branch: Vec, + path_bitmap: u32, } impl BorshSerialize for MerkleProof { @@ -136,6 +137,7 @@ impl BorshSerialize for MerkleProof { for hash in &self.branch { hash.consensus_encode(writer)?; } + BorshSerialize::serialize(&self.path_bitmap, writer)?; Ok(()) } } @@ -157,13 +159,14 @@ impl BorshDeserialize for MerkleProof { .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err.to_string()))?, ); } - Ok(Self { branch }) + let path_bitmap = BorshDeserialize::deserialize_reader(reader)?; + Ok(Self { branch, path_bitmap }) } } impl MerkleProof { - fn try_construct(branch: Vec) -> Option { - Some(Self { branch }) + fn try_construct(branch: Vec, path_bitmap: u32) -> Option { + Some(Self { branch, path_bitmap }) } /// Returns the merkle proof branch as a list of Monero hashes @@ -172,18 +175,37 @@ impl MerkleProof { &self.branch } - /// Calculates the merkle root hash from the provide Monero hash + /// returns the path bitmap of the proof + pub fn path(&self) -> u32 { + self.path_bitmap + } + /// 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 + /// is always the leftmost branch in the merkle tree, this test if the given proof is for the left most branch in + /// the merkle tree + pub fn check_coinbase_path(&self) -> bool { + if self.path_bitmap == 0b00000000 { + return true; + } + false + } + + /// Calculates the merkle root hash from the provide Monero hash + pub fn calculate_root(&self, hash: &Hash) -> Hash { if self.branch.is_empty() { return *hash; } let mut root = *hash; - for hash in &self.branch { - root = cn_fast_hash2(&root, hash); + let depth = self.branch.len(); + for d in 0..depth { + if (self.path_bitmap >> (depth - d - 1)) & 1 > 0 { + root = cn_fast_hash2(&self.branch[d], &root); + } else { + root = cn_fast_hash2(&root, &self.branch[d]); + } } root @@ -194,21 +216,32 @@ impl Default for MerkleProof { fn default() -> Self { Self { branch: vec![Hash::null()], + path_bitmap: 0, } } } /// Creates a merkle proof for the given hash within the set of hashes. This function returns None if the hash is not in -/// hashes. +/// hashes. This is a port of Monero's tree_branch function #[allow(clippy::cognitive_complexity)] -pub fn create_merkle_proof(hashes: &[Hash]) -> Option { - // Monero coinbase rules specify that the coinbase should be hash[0] +pub fn create_merkle_proof(hashes: &[Hash], hash: &Hash) -> Option { match hashes.len() { 0 => None, - 1 => MerkleProof::try_construct(vec![]), - 2 => MerkleProof::try_construct(vec![hashes[1]]), + 1 => { + if hashes[0] != *hash { + return None; + } + MerkleProof::try_construct(vec![], 0) + }, + 2 => hashes.iter().enumerate().find_map(|(pos, h)| { + if h != hash { + return None; + } + let i = usize::from(pos == 0); + MerkleProof::try_construct(vec![hashes[i]], u32::from(pos != 0)) + }), len => { - let mut idx = 0; + let mut idx = hashes.iter().position(|node| node == hash)?; let mut count = tree_hash_count(len).ok()?; let mut ints = vec![Hash::null(); count]; @@ -217,12 +250,14 @@ pub fn create_merkle_proof(hashes: &[Hash]) -> Option { ints[..c].copy_from_slice(&hashes[..c]); let mut branch = Vec::new(); + let mut path = 0u32; let mut i = c; for (j, val) in ints.iter_mut().enumerate().take(count).skip(c) { // Left or right if idx == i || idx == i + 1 { let ii = if idx == i { i + 1 } else { i }; branch.push(hashes[ii]); + path = (path << 1) | u32::from(idx != i); idx = j; } *val = cn_fast_hash2(&hashes[i], &hashes[i + 1]); @@ -238,6 +273,7 @@ pub fn create_merkle_proof(hashes: &[Hash]) -> Option { if idx == i || idx == i + 1 { let ii = if idx == i { i + 1 } else { i }; branch.push(ints[ii]); + path = (path << 1) | u32::from(idx != i); idx = j; } ints[j] = cn_fast_hash2(&ints[i], &ints[i + 1]); @@ -248,9 +284,10 @@ pub fn create_merkle_proof(hashes: &[Hash]) -> Option { if idx == 0 || idx == 1 { let ii = usize::from(idx == 0); branch.push(ints[ii]); + path = (path << 1) | u32::from(idx != 0); } - MerkleProof::try_construct(branch) + MerkleProof::try_construct(branch, path) }, } } @@ -445,16 +482,18 @@ mod test { #[test] fn empty_hashset_has_no_proof() { - assert!(create_merkle_proof(&[]).is_none()); + assert!(create_merkle_proof(&[], &Hash::null()).is_none()); } #[test] fn single_hash_is_its_own_proof() { let tx_hashes = &[Hash::from_str("fa58575f7d1d377709f1621fac98c758860ca6dc5f2262be9ce5fd131c370d1a").unwrap()]; - let proof = create_merkle_proof(&tx_hashes[..]).unwrap(); + let proof = create_merkle_proof(&tx_hashes[..], &tx_hashes[0]).unwrap(); assert_eq!(proof.branch.len(), 0); assert_eq!(proof.calculate_root(&tx_hashes[0]), tx_hashes[0]); + + assert!(create_merkle_proof(&tx_hashes[..], &Hash::null()).is_none()); } #[test] @@ -468,35 +507,76 @@ mod test { .collect::>(); let expected_root = cn_fast_hash2(&tx_hashes[0], &tx_hashes[1]); - let proof = create_merkle_proof(tx_hashes).unwrap(); + let proof = create_merkle_proof(tx_hashes, &tx_hashes[0]).unwrap(); + assert_eq!(proof.branch()[0], tx_hashes[1]); + assert_eq!(proof.branch.len(), 1); + assert_eq!(proof.branch[0], tx_hashes[1]); + assert_eq!(proof.path_bitmap, 0b00000000); assert_eq!(proof.calculate_root(&tx_hashes[0]), expected_root); + + let proof = create_merkle_proof(tx_hashes, &tx_hashes[1]).unwrap(); + assert_eq!(proof.branch()[0], tx_hashes[0]); + assert_eq!(proof.calculate_root(&tx_hashes[1]), expected_root); + + assert!(create_merkle_proof(tx_hashes, &Hash::null()).is_none()); } #[test] fn simple_proof_construction() { - // { root } - // / \ - // h0123 h4567 - // / \ / \ - // h01 h23 h45 h67 - // / \ / \ / \ / \ - // h0 h1 h2 h3 h4 h5 h6 h7 - let hashes = (1..=8).map(|i| Hash::from([i; 32])).collect::>(); + // { root } + // / \ + // h01 h2345 + // / \ / \ + // h0 h1 h23 h45 + // / \ / \ + // h2 h3 h4 h5 + + let hashes = (1..=6).map(|i| Hash::from([i - 1; 32])).collect::>(); let h23 = cn_fast_hash2(&hashes[2], &hashes[3]); let h45 = cn_fast_hash2(&hashes[4], &hashes[5]); - let h67 = cn_fast_hash2(&hashes[6], &hashes[7]); let h01 = cn_fast_hash2(&hashes[0], &hashes[1]); - let h0123 = cn_fast_hash2(&h01, &h23); - let h4567 = cn_fast_hash2(&h45, &h67); - let expected_root = cn_fast_hash2(&h0123, &h4567); + let h2345 = cn_fast_hash2(&h23, &h45); + let expected_root = cn_fast_hash2(&h01, &h2345); // Proof for h0 - let proof = create_merkle_proof(&hashes).unwrap(); + let proof = create_merkle_proof(&hashes, &hashes[0]).unwrap(); assert_eq!(proof.calculate_root(&hashes[0]), expected_root); - assert_eq!(proof.branch().len(), 3); + assert_eq!(proof.branch().len(), 2); assert_eq!(proof.branch()[0], hashes[1]); - assert_eq!(proof.branch()[1], h23); - assert_eq!(proof.branch()[2], h4567) + assert_eq!(proof.branch()[1], h2345); + assert_eq!(proof.path_bitmap, 0b00000000); + + // Proof for h2 + let proof = create_merkle_proof(&hashes, &hashes[2]).unwrap(); + assert_eq!(proof.calculate_root(&hashes[2]), expected_root); + assert_eq!(proof.path_bitmap, 0b00000001); + let branch = proof.branch(); + assert_eq!(branch[0], hashes[3]); + assert_eq!(branch[1], h45); + assert_eq!(branch[2], h01); + assert_eq!(branch.len(), 3); + + // Proof for h5 + let proof = create_merkle_proof(&hashes, &hashes[5]).unwrap(); + assert_eq!(proof.calculate_root(&hashes[5]), expected_root); + assert_eq!(proof.branch.len(), 3); + assert_eq!(proof.path_bitmap, 0b00000111); + let branch = proof.branch(); + assert_eq!(branch[0], hashes[4]); + assert_eq!(branch[1], h23); + assert_eq!(branch[2], h01); + assert_eq!(branch.len(), 3); + + // Proof for h4 + let proof = create_merkle_proof(&hashes, &hashes[4]).unwrap(); + assert_eq!(proof.calculate_root(&hashes[4]), expected_root); + assert_eq!(proof.branch.len(), 3); + assert_eq!(proof.path_bitmap, 0b00000011); + let branch = proof.branch(); + assert_eq!(branch[0], hashes[5]); + assert_eq!(branch[1], h23); + assert_eq!(branch[2], h01); + assert_eq!(branch.len(), 3); } #[test] @@ -522,8 +602,10 @@ mod test { let expected_root = tree_hash(tx_hashes).unwrap(); - let hash = Hash::from_str("d96756959949db23764592fea0bfe88c790e1fd131dabb676948b343aa9ecc24").unwrap(); - let proof = create_merkle_proof(tx_hashes).unwrap(); + let hash = Hash::from_str("fa58575f7d1d377709f1621fac98c758860ca6dc5f2262be9ce5fd131c370d1a").unwrap(); + let proof = create_merkle_proof(tx_hashes, &hash).unwrap(); + + assert_eq!(proof.path_bitmap, 0b00001111); assert_eq!(proof.calculate_root(&hash), expected_root); @@ -547,8 +629,11 @@ mod test { let expected_root = tree_hash(&tx_hashes).unwrap(); - let hash = tx_hashes.first().unwrap(); - let proof = create_merkle_proof(&tx_hashes).unwrap(); + let hash = tx_hashes.last().unwrap(); + let proof = create_merkle_proof(&tx_hashes, hash).unwrap(); + + assert_eq!(proof.branch.len(), 16); + assert_eq!(proof.path_bitmap, 0b1111_1111_1111_1111); assert_eq!(proof.calculate_root(hash), expected_root); @@ -560,7 +645,7 @@ mod test { fn test_borsh_de_serialization() { let tx_hashes = &[Hash::from_str("fa58575f7d1d377709f1621fac98c758860ca6dc5f2262be9ce5fd131c370d1a").unwrap()]; - let proof = create_merkle_proof(&tx_hashes[..]).unwrap(); + let proof = create_merkle_proof(&tx_hashes[..], &tx_hashes[0]).unwrap(); let mut buf = Vec::new(); proof.serialize(&mut buf).unwrap(); buf.extend_from_slice(&[1, 2, 3]); diff --git a/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs b/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs new file mode 100644 index 0000000000..28765715c3 --- /dev/null +++ b/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs @@ -0,0 +1,90 @@ +// Copyright 2023, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +use std::cmp::min; +use monero::VarInt; + +struct MerkleTreeParameters{ + pub number_of_chains : u8, + pub aux_nonce : u32, +} + +impl MerkleTreeParameters{ + // pub fn from_varint(merkle_tree_varint: VarInt)-> MerkleTreeParameters{ + // + // + // } +} + +pub fn get_decode_bits(num: u64) -> u8{ + let bits_num:Vec = (61..=63).rev().map(|n| ((num >> n) & 1) as u8).collect(); + bits_num.iter().fold(0,|result, &bit|{(result<<1)^bit}) +} + +pub fn get_aux_chain_count(num: u64, bits: u8) -> u8{ + let start = 60 - min(8,bits); + dbg!(start); + let bits_num:Vec = (start..=60).rev().map(|n| ((num >> n) & 1) as u8).collect(); + dbg!(&bits_num); + bits_num.iter().fold(0,|result, &bit|{(result<<1)^bit}) +} + +pub fn get_aux_noncet(num: u64, bits: u8) -> u32{ + let start = 60 - min(8,bits as u32) -32; + let end = 60 - min(8,bits as u32); + let bits_num:Vec = (start..=end).rev().map(|n| ((num >> n) & 1) as u32).collect(); + bits_num.iter().fold(0,|result, &bit|{(result<<1)^bit}) +} + +#[cfg(test)] +mod test { + use crate::proof_of_work::monero_rx::merkle_tree_parameters::{get_aux_chain_count, get_decode_bits}; + + #[test] + fn get_decode_bits_test() { + let num = 0b1100000000000000000000000000000000000000000000000000000000000101; + let bit = get_decode_bits(num); + assert_eq!(bit, 6); + + let num = 0b0100000000000000000000000000000000000000000000000000000000000101; + let bit = get_decode_bits(num); + assert_eq!(bit, 2); + + let num = 0b1110000000000000000000000000000000000000000000000000000000000101; + let bit = get_decode_bits(num); + assert_eq!(bit, 7); + + let num = 0b0011000000000000000000000000000000000000000000000000000000000101; + let bit = get_decode_bits(num); + assert_eq!(bit, 1); + } + + #[test] + fn get_decode_aux_test() { + let num = 0b1100000000000000000000000000000000000000000000000000011111111101; + let aux_number = get_aux_chain_count(num, 8); + assert_eq!(aux_number, 255); + + + } +} \ No newline at end of file diff --git a/base_layer/core/src/proof_of_work/monero_rx/mod.rs b/base_layer/core/src/proof_of_work/monero_rx/mod.rs index bc018fb1d8..74f81cfbf5 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/mod.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/mod.rs @@ -42,6 +42,8 @@ mod pow_data; pub use pow_data::MoneroPowData; mod merkle_tree; +mod merkle_tree_parameters; + pub use merkle_tree::{create_merkle_proof, tree_hash}; // Re-exports pub use monero::{ 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 35b0473c6a..6f079074d6 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 @@ -54,6 +54,9 @@ pub struct MoneroPowData { pub coinbase_merkle_proof: MerkleProof, /// Coinbase tx from Monero pub coinbase_tx: monero::Transaction, + /// aux chain merkle proof hashes + pub aux_chain_merkle_proof: MerkleProof, + } impl BorshSerialize for MoneroPowData { @@ -63,6 +66,7 @@ impl BorshSerialize for MoneroPowData { BorshSerialize::serialize(&self.transaction_count, writer)?; self.merkle_root.consensus_encode(writer)?; BorshSerialize::serialize(&self.coinbase_merkle_proof, writer)?; + BorshSerialize::serialize(&self.aux_chain_merkle_proof, writer)?; self.coinbase_tx.consensus_encode(writer)?; Ok(()) } @@ -80,6 +84,7 @@ impl BorshDeserialize for MoneroPowData { let coinbase_merkle_proof = BorshDeserialize::deserialize_reader(reader)?; let coinbase_tx = monero::Transaction::consensus_decode(reader) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))?; + let aux_chain_merkle_proof = BorshDeserialize::deserialize_reader(reader)?; Ok(Self { header, randomx_key, @@ -87,6 +92,7 @@ impl BorshDeserialize for MoneroPowData { merkle_root, coinbase_merkle_proof, coinbase_tx, + aux_chain_merkle_proof, }) } } @@ -122,10 +128,10 @@ impl MoneroPowData { } /// Returns true if the coinbase merkle proof produces the `merkle_root` hash, otherwise false - pub fn is_valid_merkle_root(&self) -> bool { + pub fn is_coinbase_valid_merkle_root(&self) -> bool { let coinbase_hash = self.coinbase_tx.hash(); let merkle_root = self.coinbase_merkle_proof.calculate_root(&coinbase_hash); - self.merkle_root == merkle_root + (self.merkle_root == merkle_root) && self.coinbase_merkle_proof.check_coinbase_path() } /// Returns the blockhashing_blob for the Monero block @@ -173,6 +179,7 @@ mod test { merkle_root: Hash::new([10; 32]), coinbase_merkle_proof: MerkleProof::default(), coinbase_tx: Transaction::default(), + aux_chain_merkle_proof: MerkleProof::default(), }; let mut buf = Vec::new(); monero_pow_data.serialize(&mut buf).unwrap(); diff --git a/base_layer/core/tests/tests/block_validation.rs b/base_layer/core/tests/tests/block_validation.rs index d7542cac2b..34669c6d06 100644 --- a/base_layer/core/tests/tests/block_validation.rs +++ b/base_layer/core/tests/tests/block_validation.rs @@ -202,7 +202,7 @@ fn add_monero_data(tblock: &mut Block, seed_key: &str) { monero_rx::insert_merge_mining_tag_into_block(&mut mblock, hash).unwrap(); let hashes = monero_rx::create_ordered_transaction_hashes_from_block(&mblock); let merkle_root = monero_rx::tree_hash(&hashes).unwrap(); - let coinbase_merkle_proof = monero_rx::create_merkle_proof(&hashes).unwrap(); + let coinbase_merkle_proof = monero_rx::create_merkle_proof(&hashes, &hashes[0]).unwrap(); #[allow(clippy::cast_possible_truncation)] let monero_data = MoneroPowData { header: mblock.header, From e99e2eb32ea5981f17a2592c74d977ec84042b47 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Wed, 22 Nov 2023 15:50:43 +0200 Subject: [PATCH 02/14] wip --- Cargo.lock | 567 +----------------- base_layer/core/Cargo.toml | 1 + .../comms_interface/inbound_handlers.rs | 3 +- .../src/proof_of_work/monero_rx/helpers.rs | 136 +++-- .../monero_rx/merkle_tree_parameters.rs | 118 +++- .../core/src/proof_of_work/monero_rx/mod.rs | 2 +- .../src/proof_of_work/monero_rx/pow_data.rs | 1 - .../src/validation/difficulty_calculator.rs | 3 +- .../header/header_full_validator.rs | 3 +- base_layer/core/src/validation/helpers.rs | 4 +- 10 files changed, 224 insertions(+), 614 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b37847c528..26df62a961 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,54 +105,12 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" -[[package]] -name = "anstream" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "utf8parse", -] - [[package]] name = "anstyle" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" -[[package]] -name = "anstyle-parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" -dependencies = [ - "windows-sys 0.48.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" -dependencies = [ - "anstyle", - "windows-sys 0.48.0", -] - [[package]] name = "anyhow" version = "1.0.75" @@ -352,15 +310,6 @@ version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" -[[package]] -name = "base64-compat" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a8d4d2746f89841e49230dd26917df1876050f95abafafbe34f47cb534b88d7" -dependencies = [ - "byteorder", -] - [[package]] name = "base64ct" version = "1.6.0" @@ -584,12 +533,6 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" -[[package]] -name = "bytecount" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" - [[package]] name = "bytemuck" version = "1.14.0" @@ -743,7 +686,7 @@ dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -822,7 +765,7 @@ checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", "bitflags 1.3.2", - "clap_derive 3.2.25", + "clap_derive", "clap_lex 0.2.4", "indexmap 1.9.3", "once_cell", @@ -838,7 +781,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" dependencies = [ "clap_builder", - "clap_derive 4.4.7", ] [[package]] @@ -847,11 +789,8 @@ version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" dependencies = [ - "anstream", "anstyle", "clap_lex 0.6.0", - "strsim", - "terminal_size", ] [[package]] @@ -867,18 +806,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "clap_derive" -version = "4.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 2.0.38", -] - [[package]] name = "clap_lex" version = "0.2.4" @@ -911,12 +838,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - [[package]] name = "config" version = "0.13.3" @@ -936,19 +857,6 @@ dependencies = [ "yaml-rust", ] -[[package]] -name = "console" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" -dependencies = [ - "encode_unicode", - "lazy_static", - "libc", - "unicode-width", - "windows-sys 0.45.0", -] - [[package]] name = "console-api" version = "0.5.0" @@ -1277,27 +1185,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "csv" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" -dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" -dependencies = [ - "memchr", -] - [[package]] name = "ctr" version = "0.9.2" @@ -1307,67 +1194,6 @@ dependencies = [ "cipher 0.4.4", ] -[[package]] -name = "cucumber" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b2b97b1b5763e57aeddbfc796f726daa8e561922e6438daffc5087b7bd03834" -dependencies = [ - "anyhow", - "async-trait", - "atty", - "clap 4.4.7", - "console", - "cucumber-codegen", - "cucumber-expressions", - "derive_more", - "drain_filter_polyfill", - "either", - "futures 0.3.29", - "gherkin", - "globwalk", - "humantime 2.1.0", - "inventory", - "itertools 0.10.5", - "junit-report", - "linked-hash-map", - "once_cell", - "regex", - "sealed 0.4.0", - "serde", - "serde_json", -] - -[[package]] -name = "cucumber-codegen" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755eae3e762505d670bbf02db048d512e79118f93f8383ae1bf6235506ed3c94" -dependencies = [ - "cucumber-expressions", - "inflections", - "itertools 0.10.5", - "proc-macro2", - "quote", - "regex", - "syn 1.0.109", - "synthez", -] - -[[package]] -name = "cucumber-expressions" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40d2fdf5e1bb4ae7e6b25c97bf9b9d249a02243fc0fbd91075592b5f00a3bc1" -dependencies = [ - "derive_more", - "either", - "nom", - "nom_locate", - "regex", - "regex-syntax 0.6.29", -] - [[package]] name = "curve25519-dalek" version = "3.2.0" @@ -1497,17 +1323,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "derive-getters" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2c35ab6e03642397cdda1dd58abbc05d418aef8e36297f336d5aba060fe8df" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "derive_builder" version = "0.12.0" @@ -1681,12 +1496,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" -[[package]] -name = "drain_filter_polyfill" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "669a445ee724c5c69b1b06fe0b63e70a1c84bc9bb7d9696cd4f4e3ec45050408" - [[package]] name = "ecdsa" version = "0.16.8" @@ -1751,12 +1560,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - [[package]] name = "encoding_rs" version = "0.8.33" @@ -1810,7 +1613,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -1847,7 +1650,7 @@ checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" dependencies = [ "cfg-if", "rustix", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -2114,23 +1917,6 @@ dependencies = [ "polyval", ] -[[package]] -name = "gherkin" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8f8f49b2b547ec22cc4d99f3bf30d4889ef0dbaa231c0736eeaf20efb5a38e" -dependencies = [ - "heck 0.4.1", - "peg", - "quote", - "serde", - "serde_json", - "syn 1.0.109", - "textwrap 0.16.0", - "thiserror", - "typed-builder", -] - [[package]] name = "gimli" version = "0.28.0" @@ -2150,30 +1936,6 @@ dependencies = [ "url", ] -[[package]] -name = "globset" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" -dependencies = [ - "aho-corasick", - "bstr", - "fnv", - "log", - "regex", -] - -[[package]] -name = "globwalk" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" -dependencies = [ - "bitflags 1.3.2", - "ignore", - "walkdir", -] - [[package]] name = "group" version = "0.13.0" @@ -2337,7 +2099,7 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -2497,23 +2259,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "ignore" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" -dependencies = [ - "globset", - "lazy_static", - "log", - "memchr", - "regex", - "same-file", - "thread_local", - "walkdir", - "winapi-util", -] - [[package]] name = "image" version = "0.23.14" @@ -2577,12 +2322,6 @@ dependencies = [ "hashbrown 0.14.2", ] -[[package]] -name = "inflections" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" - [[package]] name = "inout" version = "0.1.3" @@ -2607,12 +2346,6 @@ version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" -[[package]] -name = "inventory" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0508c56cfe9bfd5dfeb0c22ab9a6abfda2f27bdca422132e494266351ed8d83c" - [[package]] name = "ipnet" version = "2.9.0" @@ -2627,7 +2360,7 @@ checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.3", "rustix", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -2683,30 +2416,6 @@ dependencies = [ "serde", ] -[[package]] -name = "jsonrpc" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f8423b78fc94d12ef1a4a9d13c348c9a78766dda0cc18817adf0faf77e670c8" -dependencies = [ - "base64-compat", - "serde", - "serde_derive", - "serde_json", -] - -[[package]] -name = "junit-report" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06c3a3342e6720a82d7d179f380e9841b73a1dd49344e33959fdfe571ce56b55" -dependencies = [ - "derive-getters", - "quick-xml", - "strip-ansi-escapes", - "time", -] - [[package]] name = "keccak" version = "0.1.4" @@ -3400,7 +3109,7 @@ dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -3423,7 +3132,7 @@ dependencies = [ "fixed-hash", "hex", "hex-literal", - "sealed 0.4.0", + "sealed", "serde", "thiserror", "tiny-keccak", @@ -3540,17 +3249,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nom_locate" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" -dependencies = [ - "bytecount", - "memchr", - "nom", -] - [[package]] name = "ntapi" version = "0.3.7" @@ -3878,7 +3576,7 @@ dependencies = [ "libc", "redox_syscall 0.4.1", "smallvec", - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -3904,33 +3602,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" -[[package]] -name = "peg" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f76678828272f177ac33b7e2ac2e3e73cc6c1cd1e3e387928aa69562fa51367" -dependencies = [ - "peg-macros", - "peg-runtime", -] - -[[package]] -name = "peg-macros" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "636d60acf97633e48d266d7415a9355d4389cea327a193f87df395d88cd2b14d" -dependencies = [ - "peg-runtime", - "proc-macro2", - "quote", -] - -[[package]] -name = "peg-runtime" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555b1514d2d99d78150d3c799d4c357a3e2c2a8062cd108e93a06d9057629c5" - [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -4412,15 +4083,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" -[[package]] -name = "quick-xml" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" -dependencies = [ - "memchr", -] - [[package]] name = "quote" version = "1.0.33" @@ -4674,7 +4336,7 @@ dependencies = [ "libc", "spin 0.9.8", "untrusted 0.9.0", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -4768,7 +4430,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -4868,7 +4530,7 @@ version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -4902,18 +4564,6 @@ dependencies = [ "untrusted 0.9.0", ] -[[package]] -name = "sealed" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "636b9882a0f4cc2039488df89a10eb4b7976d4b6c1917fc0518f3f0f5e2c72ca" -dependencies = [ - "heck 0.3.3", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "sealed" version = "0.4.0" @@ -5183,12 +4833,6 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" -[[package]] -name = "smawk" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" - [[package]] name = "snafu" version = "0.7.5" @@ -5244,7 +4888,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -5287,15 +4931,6 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" -[[package]] -name = "strip-ansi-escapes" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ff8ef943b384c414f54aefa961dd2bd853add74ec75e7ac74cf91dba62bcfa" -dependencies = [ - "vte", -] - [[package]] name = "strsim" version = "0.10.0" @@ -5399,39 +5034,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "synthez" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "033178d0acccffc5490021657006e6a8dd586ee9dc6f7c24e7608b125e568cb1" -dependencies = [ - "syn 1.0.109", - "synthez-codegen", - "synthez-core", -] - -[[package]] -name = "synthez-codegen" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69263462a40e46960f070618e20094ce69e783a41f86e54bc75545136afd597a" -dependencies = [ - "syn 1.0.109", - "synthez-core", -] - -[[package]] -name = "synthez-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb8b5a4089fe1723279f06302afda64a5dacaa11a82bcbb4d08759590d4389d9" -dependencies = [ - "proc-macro2", - "quote", - "sealed 0.3.0", - "syn 1.0.109", -] - [[package]] name = "system-configuration" version = "0.5.1" @@ -5766,6 +5368,7 @@ dependencies = [ "serde", "serde_json", "serde_repr", + "sha2 0.10.8", "sha3", "strum", "strum_macros", @@ -6087,7 +5690,7 @@ dependencies = [ "fastrand", "redox_syscall 0.4.1", "rustix", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -6099,16 +5702,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "terminal_size" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" -dependencies = [ - "rustix", - "windows-sys 0.48.0", -] - [[package]] name = "textwrap" version = "0.11.0" @@ -6123,11 +5716,6 @@ name = "textwrap" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" -dependencies = [ - "smawk", - "unicode-linebreak", - "unicode-width", -] [[package]] name = "thiserror" @@ -6248,7 +5836,7 @@ dependencies = [ "socket2 0.5.5", "tokio-macros", "tracing", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -6633,17 +6221,6 @@ dependencies = [ "cipher 0.4.4", ] -[[package]] -name = "typed-builder" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89851716b67b937e393b3daa8423e67ddfc4bbbf1654bcf05488e95e0828db0c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "typemap-ors" version = "1.0.0" @@ -6698,12 +6275,6 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-linebreak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" - [[package]] name = "unicode-normalization" version = "0.1.22" @@ -6818,26 +6389,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "vte" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" -dependencies = [ - "utf8parse", - "vte_generate_state_changes", -] - -[[package]] -name = "vte_generate_state_changes" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" -dependencies = [ - "proc-macro2", - "quote", -] - [[package]] name = "walkdir" version = "2.4.0" @@ -7033,16 +6584,7 @@ version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", + "windows-targets", ] [[package]] @@ -7051,22 +6593,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets", ] [[package]] @@ -7075,93 +6602,51 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -7184,7 +6669,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] diff --git a/base_layer/core/Cargo.toml b/base_layer/core/Cargo.toml index c02977b202..193291462e 100644 --- a/base_layer/core/Cargo.toml +++ b/base_layer/core/Cargo.toml @@ -68,6 +68,7 @@ serde = { version = "1.0.106", features = ["derive"] } serde_json = "1.0" serde_repr = "0.1.8" sha3 = "0.10" +sha2 = "0.10" strum = "0.22" strum_macros = "0.22" thiserror = "1.0.26" diff --git a/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs b/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs index c7f598429b..be3623ccbc 100644 --- a/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs +++ b/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs @@ -501,6 +501,7 @@ where B: BlockchainBackend + 'static async fn check_min_block_difficulty(&self, new_block: &NewBlock) -> Result<(), CommsInterfaceError> { let constants = self.consensus_manager.consensus_constants(new_block.header.height); + let gen_hash = self.consensus_manager.get_genesis_block().hash().clone(); let mut min_difficulty = constants.min_pow_difficulty(new_block.header.pow.pow_algo); let mut header = self.blockchain_db.fetch_last_chain_header().await?; loop { @@ -525,7 +526,7 @@ where B: BlockchainBackend + 'static .await?; } let achieved = match new_block.header.pow_algo() { - PowAlgorithm::RandomX => randomx_difficulty(&new_block.header, &self.randomx_factory)?, + PowAlgorithm::RandomX => randomx_difficulty(&new_block.header, &self.randomx_factory, &gen_hash)?, PowAlgorithm::Sha3x => sha3x_difficulty(&new_block.header)?, }; if achieved < min_difficulty { diff --git a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs index 876e50f285..c3f115ee68 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs @@ -28,8 +28,10 @@ use monero::{ cryptonote::hash::Hashable, VarInt, }; -use tari_utilities::hex::HexError; +use primitive_types::U256; +use sha2::{Digest, Sha256}; use tari_common_types::types::FixedHash; +use tari_utilities::hex::HexError; use super::{ error::MergeMineError, @@ -40,6 +42,7 @@ use super::{ use crate::{ blocks::BlockHeader, proof_of_work::{ + monero_rx::merkle_tree_parameters::MerkleTreeParameters, randomx_factory::{RandomXFactory, RandomXVMInstance}, Difficulty, }, @@ -51,8 +54,9 @@ pub const LOG_TARGET: &str = "c::pow::monero_rx"; pub fn randomx_difficulty( header: &BlockHeader, randomx_factory: &RandomXFactory, + gen_hash: &FixedHash, ) -> Result { - let monero_pow_data = verify_header(header)?; + let monero_pow_data = verify_header(header, gen_hash)?; debug!(target: LOG_TARGET, "Valid Monero data: {}", monero_pow_data); let blockhashing_blob = monero_pow_data.to_blockhashing_blob(); let vm = randomx_factory.create(monero_pow_data.randomx_key())?; @@ -90,7 +94,7 @@ fn parse_extra_field_truncate_on_error(raw_extra_field: &RawExtraField) -> Extra /// 1. The merkle proof and coinbase hash produce a matching merkle root /// /// If these assertions pass, a valid `MoneroPowData` instance is returned -pub fn verify_header(header: &BlockHeader) -> Result { +pub fn verify_header(header: &BlockHeader, gen_hash: &FixedHash) -> Result { let monero_data = MoneroPowData::from_header(header)?; let expected_merge_mining_hash = header.merge_mining_hash(); let extra_field = ExtraField::try_parse(&monero_data.coinbase_tx.prefix.extra) @@ -108,7 +112,13 @@ pub fn verify_header(header: &BlockHeader) -> Result Result bool{ - // let merkle_parameters = merkle_tree_params. -true +fn check_aux_chains( + monero_data: &MoneroPowData, + merge_mining_params: VarInt, + aux_chain_merkle_root: &monero::Hash, + tari_hash: &FixedHash, + gen_hash: &FixedHash, +) -> bool { + let merkle_tree_params = MerkleTreeParameters::from_varint(merge_mining_params); + let hash_position = U256::from_little_endian( + &Sha256::new() + .chain_update(gen_hash) + .chain_update(merkle_tree_params.aux_nonce.to_le_bytes()) + .chain_update((109 as u8).to_le_bytes()) + .finalize() + .to_vec(), + ) + .low_u32() % + merkle_tree_params.number_of_chains as u32; + if hash_position != monero_data.aux_chain_merkle_proof.branch().len() as u32 { + return false; + } + let t_hash = monero::Hash::from_slice(tari_hash.as_slice()); + let merkle_root = monero_data.aux_chain_merkle_proof.calculate_root(&t_hash); + merkle_root == *aux_chain_merkle_root } /// Extracts the Monero block hash from the coinbase transaction's extra field @@ -180,7 +211,12 @@ pub fn serialize_monero_block_to_hex(obj: &monero::Block) -> Result Result { +pub fn construct_monero_data( + block: monero::Block, + seed: FixedByteArray, + ordered_aux_chain_hashes: Vec, + tari_hash: FixedHash, +) -> Result { let hashes = create_ordered_transaction_hashes_from_block(&block); let root = tree_hash(&hashes)?; let coinbase_merkle_proof = create_merkle_proof(&hashes, &hashes[0]).ok_or_else(|| { @@ -190,15 +226,22 @@ pub fn construct_monero_data(block: monero::Block, seed: FixedByteArray) -> Resu .to_string(), ) })?; + + let t_hash = monero::Hash::from_slice(tari_hash.as_slice()); + let aux_chain_merkle_proof = create_merkle_proof(&ordered_aux_chain_hashes, &t_hash).ok_or_else(|| { + MergeMineError::ValidationError( + "create_merkle_proof returned None, could not find tari hash in ordered aux chain hashes".to_string(), + ) + })?; #[allow(clippy::cast_possible_truncation)] Ok(MoneroPowData { header: block.header, randomx_key: seed, transaction_count: hashes.len() as u16, merkle_root: root, - coinbase_merkle_proof: coinbase_merkle_proof.clone(), + coinbase_merkle_proof, coinbase_tx: block.miner_tx, - aux_chain_merkle_proof: coinbase_merkle_proof + aux_chain_merkle_proof, }) } @@ -350,10 +393,13 @@ pub fn create_block_hashing_blob( // #[test] // fn test_monero_rs_block_serialize() { // // block with only the miner tx and no other transactions -// let hex = "0c0c94debaf805beb3489c722a285c092a32e7c6893abfc7d069699c8326fc3445a749c5276b6200000000029b892201ffdf882201b699d4c8b1ec020223df524af2a2ef5f870adb6e1ceb03a475c39f8b9ef76aa50b46ddd2a18349402b012839bfa19b7524ec7488917714c216ca254b38ed0424ca65ae828a7c006aeaf10208f5316a7f6b99cca60000"; -// // blockhashing blob for above block as accepted by monero -// let hex_blockhash_blob="0c0c94debaf805beb3489c722a285c092a32e7c6893abfc7d069699c8326fc3445a749c5276b6200000000602d0d4710e2c2d38da0cce097accdf5dc18b1d34323880c1aae90ab8f6be6e201"; -// let bytes = hex::decode(hex).unwrap(); +// let hex = +// "0c0c94debaf805beb3489c722a285c092a32e7c6893abfc7d069699c8326fc3445a749c5276b6200000000029b892201ffdf882201b699d4c8b1ec020223df524af2a2ef5f870adb6e1ceb03a475c39f8b9ef76aa50b46ddd2a18349402b012839bfa19b7524ec7488917714c216ca254b38ed0424ca65ae828a7c006aeaf10208f5316a7f6b99cca60000" +// ; // blockhashing blob for above block as accepted by monero +// let +// hex_blockhash_blob=" +// 0c0c94debaf805beb3489c722a285c092a32e7c6893abfc7d069699c8326fc3445a749c5276b6200000000602d0d4710e2c2d38da0cce097accdf5dc18b1d34323880c1aae90ab8f6be6e201" +// ; let bytes = hex::decode(hex).unwrap(); // let block = deserialize::(&bytes[..]).unwrap(); // let header = consensus::serialize::(&block.header); // let tx_count = 1 + block.tx_hashes.len() as u64; @@ -377,8 +423,9 @@ pub fn create_block_hashing_blob( // // #[test] // fn test_monero_data() { -// let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); -// let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); +// let blocktemplate_blob = +// "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000" +// .to_string(); let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); // let bytes = hex::decode(blocktemplate_blob).unwrap(); // let mut block = deserialize::(&bytes[..]).unwrap(); // let mut block_header = BlockHeader { @@ -425,17 +472,20 @@ pub fn create_block_hashing_blob( // // #[test] // fn test_input_blob() { -// let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); -// let bytes = hex::decode(blocktemplate_blob).unwrap(); +// let blocktemplate_blob = +// "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000" +// .to_string(); let bytes = hex::decode(blocktemplate_blob).unwrap(); // let block = deserialize::(&bytes[..]).unwrap(); // let input_blob = create_blockhashing_blob_from_block(&block).unwrap(); -// assert_eq!(input_blob, "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000058b030b6800d433bbcb2b560afe2a08e4dc152fa77ead96d37aaf14897d3c09601"); -// } +// assert_eq!(input_blob, +// "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000058b030b6800d433bbcb2b560afe2a08e4dc152fa77ead96d37aaf14897d3c09601" +// ); } // // #[test] // fn test_append_mm_tag() { -// let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); -// let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); +// let blocktemplate_blob = +// "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000" +// .to_string(); let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); // let bytes = hex::decode(blocktemplate_blob).unwrap(); // let mut block = deserialize::(&bytes[..]).unwrap(); // let mut block_header = BlockHeader { @@ -487,8 +537,9 @@ pub fn create_block_hashing_blob( // // #[test] // fn test_append_mm_tag_no_tag() { -// let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); -// let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); +// let blocktemplate_blob = +// "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000" +// .to_string(); let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); // let bytes = hex::decode(blocktemplate_blob).unwrap(); // let block = deserialize::(&bytes[..]).unwrap(); // let mut block_header = BlockHeader { @@ -539,8 +590,9 @@ pub fn create_block_hashing_blob( // // #[test] // fn test_append_mm_tag_wrong_hash() { -// let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); -// let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); +// let blocktemplate_blob = +// "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000" +// .to_string(); let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); // let bytes = hex::decode(blocktemplate_blob).unwrap(); // let mut block = deserialize::(&bytes[..]).unwrap(); // let mut block_header = BlockHeader { @@ -595,8 +647,9 @@ pub fn create_block_hashing_blob( // // #[test] // fn test_duplicate_append_mm_tag() { -// let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); -// let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); +// let blocktemplate_blob = +// "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000" +// .to_string(); let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); // let bytes = hex::decode(blocktemplate_blob).unwrap(); // let mut block = deserialize::(&bytes[..]).unwrap(); // let mut block_header = BlockHeader { @@ -673,8 +726,9 @@ pub fn create_block_hashing_blob( // // #[test] // fn test_extra_field_with_parsing_error() { -// let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); -// let bytes = hex::decode(blocktemplate_blob).unwrap(); +// let blocktemplate_blob = +// "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000" +// .to_string(); let bytes = hex::decode(blocktemplate_blob).unwrap(); // let mut block = deserialize::(&bytes[..]).unwrap(); // let block_header = BlockHeader { // version: 0, @@ -705,14 +759,14 @@ pub fn create_block_hashing_blob( // // extra_field_before_parse.0.insert(0, SubField::Padding(230)); // assert_eq!( -// "ExtraField([Padding(230), TxPublicKey(06225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa4782), \ -// Nonce([246, 58, 168, 109, 46, 133, 127, 7])])", +// "ExtraField([Padding(230), TxPublicKey(06225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa4782), +// \ Nonce([246, 58, 168, 109, 46, 133, 127, 7])])", // &format!("{:?}", extra_field_before_parse) // ); // assert!(ExtraField::try_parse(&extra_field_before_parse.clone().into()).is_err()); // -// // Now insert the merge mining tag - this would also clean up the extra field and remove the invalid sub-fields -// let hash = block_header.merge_mining_hash(); +// // Now insert the merge mining tag - this would also clean up the extra field and remove the invalid +// sub-fields let hash = block_header.merge_mining_hash(); // insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); // assert!(ExtraField::try_parse(&block.miner_tx.prefix.extra.clone()).is_ok()); // @@ -721,8 +775,8 @@ pub fn create_block_hashing_blob( // assert_eq!( // &format!( // "ExtraField([MergeMining(Some(0), 0x{}), \ -// TxPublicKey(06225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa4782), Nonce([246, 58, 168, \ -// 109, 46, 133, 127, 7])])", +// TxPublicKey(06225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa4782), Nonce([246, 58, 168, +// \ 109, 46, 133, 127, 7])])", // hex::encode(hash) // ), // &format!("{:?}", extra_field_after_tag) @@ -731,8 +785,9 @@ pub fn create_block_hashing_blob( // // #[test] // fn test_verify_header_no_coinbase() { -// let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); -// let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); +// let blocktemplate_blob = +// "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000" +// .to_string(); let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); // let bytes = hex::decode(blocktemplate_blob).unwrap(); // let mut block = deserialize::(&bytes[..]).unwrap(); // let mut block_header = BlockHeader { @@ -826,8 +881,9 @@ pub fn create_block_hashing_blob( // // #[test] // fn test_verify_invalid_root() { -// let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); -// let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); +// let blocktemplate_blob = +// "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000" +// .to_string(); let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); // let bytes = hex::decode(blocktemplate_blob).unwrap(); // let mut block = deserialize::(&bytes[..]).unwrap(); // let mut block_header = BlockHeader { diff --git a/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs b/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs index 28765715c3..1def68b717 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs @@ -20,45 +20,52 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - use std::cmp::min; + use monero::VarInt; -struct MerkleTreeParameters{ - pub number_of_chains : u8, - pub aux_nonce : u32, +pub struct MerkleTreeParameters { + pub number_of_chains: u8, + pub aux_nonce: u32, } -impl MerkleTreeParameters{ - // pub fn from_varint(merkle_tree_varint: VarInt)-> MerkleTreeParameters{ - // - // - // } +impl MerkleTreeParameters { + pub fn from_varint(merkle_tree_varint: VarInt) -> MerkleTreeParameters { + let bits = get_decode_bits(merkle_tree_varint.0); + let number_of_chains = get_aux_chain_count(merkle_tree_varint.0, bits); + let aux_nonce = get_aux_nonce(merkle_tree_varint.0, bits); + MerkleTreeParameters { + number_of_chains, + aux_nonce, + } + } } -pub fn get_decode_bits(num: u64) -> u8{ - let bits_num:Vec = (61..=63).rev().map(|n| ((num >> n) & 1) as u8).collect(); - bits_num.iter().fold(0,|result, &bit|{(result<<1)^bit}) +pub fn get_decode_bits(num: u64) -> u8 { + let bits_num: Vec = (61..=63).rev().map(|n| ((num >> n) & 1) as u8).collect(); + bits_num.iter().fold(0, |result, &bit| (result << 1) ^ bit) } -pub fn get_aux_chain_count(num: u64, bits: u8) -> u8{ - let start = 60 - min(8,bits); - dbg!(start); - let bits_num:Vec = (start..=60).rev().map(|n| ((num >> n) & 1) as u8).collect(); - dbg!(&bits_num); - bits_num.iter().fold(0,|result, &bit|{(result<<1)^bit}) +pub fn get_aux_chain_count(num: u64, bits: u8) -> u8 { + let start = 60 - min(8, bits) + 1; + let bits_num: Vec = (start..=60).rev().map(|n| ((num >> n) & 1) as u8).collect(); + bits_num.iter().fold(0, |result, &bit| (result << 1) ^ bit) } -pub fn get_aux_noncet(num: u64, bits: u8) -> u32{ - let start = 60 - min(8,bits as u32) -32; - let end = 60 - min(8,bits as u32); - let bits_num:Vec = (start..=end).rev().map(|n| ((num >> n) & 1) as u32).collect(); - bits_num.iter().fold(0,|result, &bit|{(result<<1)^bit}) +pub fn get_aux_nonce(num: u64, bits: u8) -> u32 { + let start = 60 - min(8, bits as u32) + 1 - 32; + let end = 60 - min(8, bits as u32); + let bits_num: Vec = (start..=end).rev().map(|n| ((num >> n) & 1) as u32).collect(); + bits_num.iter().fold(0, |result, &bit| (result << 1) ^ bit) } #[cfg(test)] mod test { - use crate::proof_of_work::monero_rx::merkle_tree_parameters::{get_aux_chain_count, get_decode_bits}; + use crate::proof_of_work::monero_rx::merkle_tree_parameters::{ + get_aux_chain_count, + get_aux_nonce, + get_decode_bits, + }; #[test] fn get_decode_bits_test() { @@ -80,11 +87,68 @@ mod test { } #[test] - fn get_decode_aux_test() { - let num = 0b1100000000000000000000000000000000000000000000000000011111111101; + fn get_decode_aux_chain_test() { + let num = 0b1101111111100000000000000000000000000000000000000000000000000101; let aux_number = get_aux_chain_count(num, 8); assert_eq!(aux_number, 255); + let num = 0b1100000000100000000000000000000000000000000000000000000000000101; + let aux_number = get_aux_chain_count(num, 8); + assert_eq!(aux_number, 1); + + let num = 0b1100000000000000000000000000000000000000000000000000000000000101; + let aux_number = get_aux_chain_count(num, 8); + assert_eq!(aux_number, 0); + + let num = 0b1100111000000000000000000000000000000000000000000000000000000101; + let aux_number = get_aux_chain_count(num, 8); + assert_eq!(aux_number, 112); + + let num = 0b1100000100000000000000000000000000000000000000000000000000000101; + let aux_number = get_aux_chain_count(num, 8); + assert_eq!(aux_number, 8); + + let num = 0b1100000001000000000000000000000000000000000000000000000000000101; + let aux_number = get_aux_chain_count(num, 7); + assert_eq!(aux_number, 1); + let num = 0b1100000010000000000000000000000000000000000000000000000000000101; + let aux_number = get_aux_chain_count(num, 6); + assert_eq!(aux_number, 1); + + let num = 0b1100000100000000000000000000000000000000000000000000000000000101; + let aux_number = get_aux_chain_count(num, 5); + assert_eq!(aux_number, 1); + + let num = 0b1100000110000000000000000000000000000000000000000000000000000101; + let aux_number = get_aux_chain_count(num, 5); + assert_eq!(aux_number, 1); + + let num = 0b1111000110000000000000000000000000000000000000000000000000000101; + let aux_number = get_aux_chain_count(num, 1); + assert_eq!(aux_number, 1); } -} \ No newline at end of file + + #[test] + fn get_decode_aux_nonce_test() { + let num = 0b1100000000110000000000000000000000000000000000000000000000000101; + let aux_number = get_aux_nonce(num, 8); + assert_eq!(aux_number, 2147483648); + + let num = 0b1100000000011111111111111111111111111111111000000000000000000101; + let aux_number = get_aux_nonce(num, 8); + assert_eq!(aux_number, u32::MAX); + + let num = 0b1100000000111111111111111111111111111111110000000000000000000101; + let aux_number = get_aux_nonce(num, 7); + assert_eq!(aux_number, u32::MAX); + + let num = 0b1100111111111111111111111111111111110000000000000000000000000101; + let aux_number = get_aux_nonce(num, 1); + assert_eq!(aux_number, u32::MAX); + + let num = 0b1100000000100000000000000000000000000000001000000000000000000101; + let aux_number = get_aux_nonce(num, 8); + assert_eq!(aux_number, 1); + } +} diff --git a/base_layer/core/src/proof_of_work/monero_rx/mod.rs b/base_layer/core/src/proof_of_work/monero_rx/mod.rs index 74f81cfbf5..773681847a 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/mod.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/mod.rs @@ -43,8 +43,8 @@ pub use pow_data::MoneroPowData; mod merkle_tree; mod merkle_tree_parameters; - pub use merkle_tree::{create_merkle_proof, tree_hash}; +pub use merkle_tree_parameters::MerkleTreeParameters; // Re-exports pub use monero::{ consensus::{deserialize, serialize}, 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 6f079074d6..47a1f43a22 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 @@ -56,7 +56,6 @@ pub struct MoneroPowData { pub coinbase_tx: monero::Transaction, /// aux chain merkle proof hashes pub aux_chain_merkle_proof: MerkleProof, - } impl BorshSerialize for MoneroPowData { diff --git a/base_layer/core/src/validation/difficulty_calculator.rs b/base_layer/core/src/validation/difficulty_calculator.rs index fc2f297311..772f1630d8 100644 --- a/base_layer/core/src/validation/difficulty_calculator.rs +++ b/base_layer/core/src/validation/difficulty_calculator.rs @@ -51,7 +51,8 @@ impl DifficultyCalculator { constants.min_pow_difficulty(block_header.pow.pow_algo), constants.max_pow_difficulty(block_header.pow.pow_algo), ); - let achieved_target = check_target_difficulty(block_header, target, &self.randomx_factory)?; + let gen_hash = self.rules.get_genesis_block().hash().clone(); + let achieved_target = check_target_difficulty(block_header, target, &self.randomx_factory, &gen_hash)?; Ok(achieved_target) } diff --git a/base_layer/core/src/validation/header/header_full_validator.rs b/base_layer/core/src/validation/header/header_full_validator.rs index 46a8262ee1..d091edb7ea 100644 --- a/base_layer/core/src/validation/header/header_full_validator.rs +++ b/base_layer/core/src/validation/header/header_full_validator.rs @@ -77,9 +77,10 @@ impl HeaderChainLinkedValidator for HeaderFullValidator check_timestamp_ftl(header, &self.rules)?; check_pow_data(header, &self.rules, db)?; + let gen_hash = self.rules.get_genesis_block().hash().clone(); let achieved_target = if let Some(target) = target_difficulty { - check_target_difficulty(header, target, &self.difficulty_calculator.randomx_factory)? + check_target_difficulty(header, target, &self.difficulty_calculator.randomx_factory, &gen_hash)? } else { self.difficulty_calculator .check_achieved_and_target_difficulty(db, header)? diff --git a/base_layer/core/src/validation/helpers.rs b/base_layer/core/src/validation/helpers.rs index c0f371837b..2df8a469a8 100644 --- a/base_layer/core/src/validation/helpers.rs +++ b/base_layer/core/src/validation/helpers.rs @@ -23,6 +23,7 @@ use std::convert::TryFrom; use log::*; +use tari_common_types::types::FixedHash; use tari_crypto::tari_utilities::{epoch_time::EpochTime, hex::Hex}; use tari_script::TariScript; @@ -119,9 +120,10 @@ pub fn check_target_difficulty( block_header: &BlockHeader, target: Difficulty, randomx_factory: &RandomXFactory, + gen_hash: &FixedHash, ) -> Result { let achieved = match block_header.pow_algo() { - PowAlgorithm::RandomX => randomx_difficulty(block_header, randomx_factory)?, + PowAlgorithm::RandomX => randomx_difficulty(block_header, randomx_factory, gen_hash)?, PowAlgorithm::Sha3x => sha3x_difficulty(block_header)?, }; From 029e3b62ef5127bafb91beb9cc57f060124c3a45 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Thu, 23 Nov 2023 14:47:28 +0200 Subject: [PATCH 03/14] added proxy --- Cargo.lock | 567 +++++++++++++++++- .../minotari_merge_mining_proxy/Cargo.toml | 1 + .../src/block_template_data.rs | 28 +- .../src/block_template_protocol.rs | 32 +- .../minotari_merge_mining_proxy/src/proxy.rs | 39 +- .../src/run_merge_miner.rs | 2 +- .../comms_interface/inbound_handlers.rs | 2 +- .../src/proof_of_work/monero_rx/helpers.rs | 32 +- .../proof_of_work/monero_rx/merkle_tree.rs | 13 +- .../monero_rx/merkle_tree_parameters.rs | 103 +++- .../core/src/proof_of_work/monero_rx/mod.rs | 4 +- .../src/proof_of_work/monero_rx/pow_data.rs | 2 +- .../src/validation/difficulty_calculator.rs | 2 +- .../header/header_full_validator.rs | 2 +- .../core/tests/tests/block_validation.rs | 10 +- 15 files changed, 762 insertions(+), 77 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 26df62a961..3e6722b1c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,12 +105,54 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" +[[package]] +name = "anstream" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + [[package]] name = "anstyle" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +[[package]] +name = "anstyle-parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + [[package]] name = "anyhow" version = "1.0.75" @@ -310,6 +352,15 @@ version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +[[package]] +name = "base64-compat" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a8d4d2746f89841e49230dd26917df1876050f95abafafbe34f47cb534b88d7" +dependencies = [ + "byteorder", +] + [[package]] name = "base64ct" version = "1.6.0" @@ -533,6 +584,12 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +[[package]] +name = "bytecount" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" + [[package]] name = "bytemuck" version = "1.14.0" @@ -686,7 +743,7 @@ dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -765,7 +822,7 @@ checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", "bitflags 1.3.2", - "clap_derive", + "clap_derive 3.2.25", "clap_lex 0.2.4", "indexmap 1.9.3", "once_cell", @@ -781,6 +838,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" dependencies = [ "clap_builder", + "clap_derive 4.4.7", ] [[package]] @@ -789,8 +847,11 @@ version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" dependencies = [ + "anstream", "anstyle", "clap_lex 0.6.0", + "strsim", + "terminal_size", ] [[package]] @@ -806,6 +867,18 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "clap_lex" version = "0.2.4" @@ -838,6 +911,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "config" version = "0.13.3" @@ -857,6 +936,19 @@ dependencies = [ "yaml-rust", ] +[[package]] +name = "console" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.45.0", +] + [[package]] name = "console-api" version = "0.5.0" @@ -1185,6 +1277,27 @@ dependencies = [ "typenum", ] +[[package]] +name = "csv" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +dependencies = [ + "memchr", +] + [[package]] name = "ctr" version = "0.9.2" @@ -1194,6 +1307,67 @@ dependencies = [ "cipher 0.4.4", ] +[[package]] +name = "cucumber" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b2b97b1b5763e57aeddbfc796f726daa8e561922e6438daffc5087b7bd03834" +dependencies = [ + "anyhow", + "async-trait", + "atty", + "clap 4.4.7", + "console", + "cucumber-codegen", + "cucumber-expressions", + "derive_more", + "drain_filter_polyfill", + "either", + "futures 0.3.29", + "gherkin", + "globwalk", + "humantime 2.1.0", + "inventory", + "itertools 0.10.5", + "junit-report", + "linked-hash-map", + "once_cell", + "regex", + "sealed 0.4.0", + "serde", + "serde_json", +] + +[[package]] +name = "cucumber-codegen" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "755eae3e762505d670bbf02db048d512e79118f93f8383ae1bf6235506ed3c94" +dependencies = [ + "cucumber-expressions", + "inflections", + "itertools 0.10.5", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "synthez", +] + +[[package]] +name = "cucumber-expressions" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40d2fdf5e1bb4ae7e6b25c97bf9b9d249a02243fc0fbd91075592b5f00a3bc1" +dependencies = [ + "derive_more", + "either", + "nom", + "nom_locate", + "regex", + "regex-syntax 0.6.29", +] + [[package]] name = "curve25519-dalek" version = "3.2.0" @@ -1323,6 +1497,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive-getters" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2c35ab6e03642397cdda1dd58abbc05d418aef8e36297f336d5aba060fe8df" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "derive_builder" version = "0.12.0" @@ -1496,6 +1681,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "drain_filter_polyfill" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "669a445ee724c5c69b1b06fe0b63e70a1c84bc9bb7d9696cd4f4e3ec45050408" + [[package]] name = "ecdsa" version = "0.16.8" @@ -1560,6 +1751,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "encoding_rs" version = "0.8.33" @@ -1613,7 +1810,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1650,7 +1847,7 @@ checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" dependencies = [ "cfg-if", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1917,6 +2114,23 @@ dependencies = [ "polyval", ] +[[package]] +name = "gherkin" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8f8f49b2b547ec22cc4d99f3bf30d4889ef0dbaa231c0736eeaf20efb5a38e" +dependencies = [ + "heck 0.4.1", + "peg", + "quote", + "serde", + "serde_json", + "syn 1.0.109", + "textwrap 0.16.0", + "thiserror", + "typed-builder", +] + [[package]] name = "gimli" version = "0.28.0" @@ -1936,6 +2150,30 @@ dependencies = [ "url", ] +[[package]] +name = "globset" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", +] + [[package]] name = "group" version = "0.13.0" @@ -2099,7 +2337,7 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2259,6 +2497,23 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "ignore" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" +dependencies = [ + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + [[package]] name = "image" version = "0.23.14" @@ -2322,6 +2577,12 @@ dependencies = [ "hashbrown 0.14.2", ] +[[package]] +name = "inflections" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" + [[package]] name = "inout" version = "0.1.3" @@ -2346,6 +2607,12 @@ version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" +[[package]] +name = "inventory" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0508c56cfe9bfd5dfeb0c22ab9a6abfda2f27bdca422132e494266351ed8d83c" + [[package]] name = "ipnet" version = "2.9.0" @@ -2360,7 +2627,7 @@ checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.3", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2416,6 +2683,30 @@ dependencies = [ "serde", ] +[[package]] +name = "jsonrpc" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f8423b78fc94d12ef1a4a9d13c348c9a78766dda0cc18817adf0faf77e670c8" +dependencies = [ + "base64-compat", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "junit-report" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c3a3342e6720a82d7d179f380e9841b73a1dd49344e33959fdfe571ce56b55" +dependencies = [ + "derive-getters", + "quick-xml", + "strip-ansi-escapes", + "time", +] + [[package]] name = "keccak" version = "0.1.4" @@ -2863,6 +3154,7 @@ dependencies = [ "minotari_app_utilities", "minotari_node_grpc_client", "minotari_wallet_grpc_client", + "monero", "reqwest", "serde", "serde_json", @@ -3109,7 +3401,7 @@ dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -3132,7 +3424,7 @@ dependencies = [ "fixed-hash", "hex", "hex-literal", - "sealed", + "sealed 0.4.0", "serde", "thiserror", "tiny-keccak", @@ -3249,6 +3541,17 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom_locate" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" +dependencies = [ + "bytecount", + "memchr", + "nom", +] + [[package]] name = "ntapi" version = "0.3.7" @@ -3576,7 +3879,7 @@ dependencies = [ "libc", "redox_syscall 0.4.1", "smallvec", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -3602,6 +3905,33 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +[[package]] +name = "peg" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f76678828272f177ac33b7e2ac2e3e73cc6c1cd1e3e387928aa69562fa51367" +dependencies = [ + "peg-macros", + "peg-runtime", +] + +[[package]] +name = "peg-macros" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "636d60acf97633e48d266d7415a9355d4389cea327a193f87df395d88cd2b14d" +dependencies = [ + "peg-runtime", + "proc-macro2", + "quote", +] + +[[package]] +name = "peg-runtime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555b1514d2d99d78150d3c799d4c357a3e2c2a8062cd108e93a06d9057629c5" + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -4083,6 +4413,15 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" +[[package]] +name = "quick-xml" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.33" @@ -4336,7 +4675,7 @@ dependencies = [ "libc", "spin 0.9.8", "untrusted 0.9.0", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -4430,7 +4769,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -4530,7 +4869,7 @@ version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -4564,6 +4903,18 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "sealed" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "636b9882a0f4cc2039488df89a10eb4b7976d4b6c1917fc0518f3f0f5e2c72ca" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "sealed" version = "0.4.0" @@ -4833,6 +5184,12 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + [[package]] name = "snafu" version = "0.7.5" @@ -4888,7 +5245,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -4931,6 +5288,15 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" +[[package]] +name = "strip-ansi-escapes" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ff8ef943b384c414f54aefa961dd2bd853add74ec75e7ac74cf91dba62bcfa" +dependencies = [ + "vte", +] + [[package]] name = "strsim" version = "0.10.0" @@ -5034,6 +5400,39 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "synthez" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "033178d0acccffc5490021657006e6a8dd586ee9dc6f7c24e7608b125e568cb1" +dependencies = [ + "syn 1.0.109", + "synthez-codegen", + "synthez-core", +] + +[[package]] +name = "synthez-codegen" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69263462a40e46960f070618e20094ce69e783a41f86e54bc75545136afd597a" +dependencies = [ + "syn 1.0.109", + "synthez-core", +] + +[[package]] +name = "synthez-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb8b5a4089fe1723279f06302afda64a5dacaa11a82bcbb4d08759590d4389d9" +dependencies = [ + "proc-macro2", + "quote", + "sealed 0.3.0", + "syn 1.0.109", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -5690,7 +6089,7 @@ dependencies = [ "fastrand", "redox_syscall 0.4.1", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -5702,6 +6101,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal_size" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +dependencies = [ + "rustix", + "windows-sys 0.48.0", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -5716,6 +6125,11 @@ name = "textwrap" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] [[package]] name = "thiserror" @@ -5836,7 +6250,7 @@ dependencies = [ "socket2 0.5.5", "tokio-macros", "tracing", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -6221,6 +6635,17 @@ dependencies = [ "cipher 0.4.4", ] +[[package]] +name = "typed-builder" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89851716b67b937e393b3daa8423e67ddfc4bbbf1654bcf05488e95e0828db0c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "typemap-ors" version = "1.0.0" @@ -6275,6 +6700,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + [[package]] name = "unicode-normalization" version = "0.1.22" @@ -6389,6 +6820,26 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vte" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" +dependencies = [ + "utf8parse", + "vte_generate_state_changes", +] + +[[package]] +name = "vte_generate_state_changes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "walkdir" version = "2.4.0" @@ -6584,7 +7035,16 @@ version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", ] [[package]] @@ -6593,7 +7053,22 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -6602,51 +7077,93 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -6669,7 +7186,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] diff --git a/applications/minotari_merge_mining_proxy/Cargo.toml b/applications/minotari_merge_mining_proxy/Cargo.toml index a4886f2f1d..27b8c90a37 100644 --- a/applications/minotari_merge_mining_proxy/Cargo.toml +++ b/applications/minotari_merge_mining_proxy/Cargo.toml @@ -35,6 +35,7 @@ hex = "0.4.2" hyper = "0.14.12" jsonrpc = "0.12.0" log = { version = "0.4.8", features = ["std"] } +monero = { version = "0.18" } reqwest = { version = "0.11.4", features = ["json"] } serde = { version = "1.0.106", features = ["derive"] } serde_json = "1.0.57" diff --git a/applications/minotari_merge_mining_proxy/src/block_template_data.rs b/applications/minotari_merge_mining_proxy/src/block_template_data.rs index 3ee6c19350..d88598dda4 100644 --- a/applications/minotari_merge_mining_proxy/src/block_template_data.rs +++ b/applications/minotari_merge_mining_proxy/src/block_template_data.rs @@ -28,6 +28,7 @@ use std::{collections::HashMap, sync::Arc}; use chrono::Duration; use chrono::{self, DateTime, Utc}; use minotari_node_grpc_client::grpc; +use tari_common_types::types::FixedHash; use tari_core::proof_of_work::monero_rx::FixedByteArray; use tokio::sync::RwLock; use tracing::trace; @@ -125,6 +126,8 @@ pub struct BlockTemplateData { pub tari_miner_data: grpc::MinerData, pub monero_difficulty: u64, pub tari_difficulty: u64, + pub tari_hash: FixedHash, + pub aux_chain_hashes: Vec, } impl BlockTemplateData {} @@ -137,6 +140,8 @@ pub struct BlockTemplateDataBuilder { tari_miner_data: Option, monero_difficulty: Option, tari_difficulty: Option, + tari_hash: Option, + aux_chain_hashes: Vec, } impl BlockTemplateDataBuilder { @@ -169,6 +174,16 @@ impl BlockTemplateDataBuilder { self } + pub fn tari_hash(mut self, hash: FixedHash) -> Self { + self.tari_hash = Some(hash); + self + } + + pub fn aux_hashes(mut self, aux_chain_hashes: Vec) -> Self { + self.aux_chain_hashes = aux_chain_hashes; + self + } + /// Build a new [BlockTemplateData], all the values have to be set. /// /// # Errors @@ -190,6 +205,12 @@ impl BlockTemplateDataBuilder { let tari_difficulty = self .tari_difficulty .ok_or_else(|| MmProxyError::MissingDataError("tari_difficulty not provided".to_string()))?; + let tari_hash = self + .tari_hash + .ok_or_else(|| MmProxyError::MissingDataError("tari_hash not provided".to_string()))?; + if self.aux_chain_hashes.is_empty() { + return Err(MmProxyError::MissingDataError("aux chain hashes are empty".to_string())); + }; Ok(BlockTemplateData { monero_seed, @@ -197,6 +218,8 @@ impl BlockTemplateDataBuilder { tari_miner_data, monero_difficulty, tari_difficulty, + tari_hash, + aux_chain_hashes: self.aux_chain_hashes, }) } } @@ -216,6 +239,7 @@ pub mod test { let header = BlockHeader::new(100); let body = AggregateBody::empty(); let block = Block::new(header, body); + let hash = block.hash(); let miner_data = grpc::MinerData { reward: 10000, target_difficulty: 600000, @@ -227,7 +251,9 @@ pub mod test { .tari_block(block.try_into().unwrap()) .tari_miner_data(miner_data) .monero_difficulty(123456) - .tari_difficulty(12345); + .tari_difficulty(12345) + .tari_hash(hash) + .aux_hashes(vec![monero::Hash::from_slice(hash.as_slice())]); btdb.build().unwrap() } diff --git a/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs b/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs index 8cd1d6496c..9d62a85891 100644 --- a/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs +++ b/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs @@ -21,13 +21,12 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //! Methods for seting up a new block. - -use std::{cmp, str::FromStr, sync::Arc}; +use std::{cmp, convert::TryFrom, str::FromStr, sync::Arc}; use log::*; use minotari_app_grpc::{authentication::ClientAuthenticationInterceptor, tari_rpc::base_node_client::BaseNodeClient}; use minotari_node_grpc_client::grpc; -use tari_common_types::tari_address::TariAddress; +use tari_common_types::{tari_address::TariAddress, types::FixedHash}; use tari_core::{ consensus::ConsensusManager, proof_of_work::{monero_rx, monero_rx::FixedByteArray, Difficulty}, @@ -61,11 +60,11 @@ impl<'a> BlockTemplateProtocol<'a> { pub async fn new( base_node_client: &'a mut BaseNodeClient>, config: Arc, + consensus_manager: ConsensusManager, ) -> Result, MmProxyError> { let key_manager = create_memory_db_key_manager(); let wallet_payment_address = TariAddress::from_str(&config.wallet_payment_address) .map_err(|err| MmProxyError::WalletPaymentAddress(err.to_string()))?; - let consensus_manager = ConsensusManager::builder(config.network).build()?; Ok(Self { config, base_node_client, @@ -242,6 +241,11 @@ impl BlockTemplateProtocol<'_> { .monero_seed(monero_mining_data.seed_hash) .monero_difficulty(monero_mining_data.difficulty) .tari_difficulty(tari_difficulty) + .tari_hash( + FixedHash::try_from(tari_block.merge_mining_hash.clone()) + .map_err(|e| MmProxyError::MissingDataError(e.to_string()))?, + ) + .aux_hashes(vec![monero::Hash::from_slice(&tari_block.merge_mining_hash)]) .build()?; // Deserialize the block template blob @@ -250,7 +254,15 @@ impl BlockTemplateProtocol<'_> { debug!(target: LOG_TARGET, "Insert Merged Mining Tag",); // Add the Tari merge mining tag to the retrieved block template - monero_rx::insert_merge_mining_tag_into_block(&mut monero_block, &tari_block.merge_mining_hash)?; + // We need to send the MR al all aux chains, but a single chain, aka minotari only, means we only need the tari + // hash + let aux_chain_mr = tari_block.merge_mining_hash.clone(); + monero_rx::insert_merge_mining_tag_and_aux_chain_merkle_root_into_block( + &mut monero_block, + &aux_chain_mr, + 1, + 0, + )?; debug!(target: LOG_TARGET, "Creating blockhashing blob from blocktemplate blob",); // Must be done after the tag is inserted since it will affect the hash of the miner tx @@ -266,12 +278,16 @@ impl BlockTemplateProtocol<'_> { monero_mining_data.difficulty, mining_difficulty ); + let merge_mining_hash = FixedHash::try_from(tari_block.merge_mining_hash.clone()) + .map_err(|e| MmProxyError::MissingDataError(e.to_string()))?; Ok(FinalBlockTemplateData { template: block_template_data, target_difficulty: Difficulty::from_u64(mining_difficulty)?, blockhashing_blob, blocktemplate_blob, - merge_mining_hash: tari_block.merge_mining_hash, + merge_mining_hash, + aux_chain_hashes: vec![monero::Hash::from_slice(&tari_block.merge_mining_hash)], + aux_chain_mr: tari_block.merge_mining_hash, }) } } @@ -296,7 +312,9 @@ pub struct FinalBlockTemplateData { pub target_difficulty: Difficulty, pub blockhashing_blob: String, pub blocktemplate_blob: String, - pub merge_mining_hash: Vec, + pub merge_mining_hash: FixedHash, + pub aux_chain_hashes: Vec, + pub aux_chain_mr: Vec, } /// Container struct for monero mining data inputs obtained from monerod diff --git a/applications/minotari_merge_mining_proxy/src/proxy.rs b/applications/minotari_merge_mining_proxy/src/proxy.rs index bde3e7917b..3f062d18c7 100644 --- a/applications/minotari_merge_mining_proxy/src/proxy.rs +++ b/applications/minotari_merge_mining_proxy/src/proxy.rs @@ -43,11 +43,9 @@ use minotari_node_grpc_client::{grpc, grpc::base_node_client::BaseNodeClient}; use minotari_wallet_grpc_client::ClientAuthenticationInterceptor; use reqwest::{ResponseBuilderExt, Url}; use serde_json as json; -use tari_core::proof_of_work::{ - monero_rx, - monero_rx::FixedByteArray, - randomx_difficulty, - randomx_factory::RandomXFactory, +use tari_core::{ + consensus::ConsensusManager, + proof_of_work::{monero_rx, monero_rx::FixedByteArray, randomx_difficulty, randomx_factory::RandomXFactory}, }; use tari_utilities::hex::Hex; use tonic::{codegen::InterceptedService, transport::Channel}; @@ -79,9 +77,10 @@ impl MergeMiningProxyService { base_node_client: BaseNodeClient>, block_templates: BlockTemplateRepository, randomx_factory: RandomXFactory, - ) -> Self { + ) -> Result { debug!(target: LOG_TARGET, "Config: {:?}", config); - Self { + let consensus_manager = ConsensusManager::builder(config.network).build()?; + Ok(Self { inner: InnerService { config: Arc::new(config), block_templates, @@ -91,8 +90,9 @@ impl MergeMiningProxyService { current_monerod_server: Arc::new(RwLock::new(None)), last_assigned_monerod_server: Arc::new(RwLock::new(None)), randomx_factory, + consensus_manager, }, - } + }) } } @@ -161,6 +161,7 @@ struct InnerService { current_monerod_server: Arc>>, last_assigned_monerod_server: Arc>>, randomx_factory: RandomXFactory, + consensus_manager: ConsensusManager, } impl InnerService { @@ -238,10 +239,12 @@ impl InnerService { }, }; + let gen_hash = *self.consensus_manager.get_genesis_block().hash(); + for param in params.iter().filter_map(|p| p.as_str()) { let monero_block = monero_rx::deserialize_monero_block_from_hex(param)?; debug!(target: LOG_TARGET, "Monero block: {}", monero_block); - let hash = monero_rx::extract_tari_hash_from_block(&monero_block)?.ok_or_else(|| { + let hash = monero_rx::extract_aux_merkle_root_from_block(&monero_block)?.ok_or_else(|| { MmProxyError::MissingDataError("Could not find Minotari header in coinbase".to_string()) })?; @@ -262,8 +265,12 @@ impl InnerService { continue; }, }; - - let monero_data = monero_rx::construct_monero_data(monero_block, block_data.monero_seed.clone())?; + let monero_data = monero_rx::construct_monero_data( + monero_block, + block_data.monero_seed.clone(), + block_data.aux_chain_hashes.clone(), + block_data.tari_hash, + )?; debug!(target: LOG_TARGET, "Monero PoW Data: {:?}", monero_data); @@ -276,7 +283,7 @@ impl InnerService { let start = Instant::now(); let achieved_target = if self.config.check_tari_difficulty_before_submit { trace!(target: LOG_TARGET, "Starting calculate achieved Tari difficultly"); - let diff = randomx_difficulty(&tari_header, &self.randomx_factory)?; + let diff = randomx_difficulty(&tari_header, &self.randomx_factory, &gen_hash)?; trace!( target: LOG_TARGET, "Finished calculate achieved Tari difficultly - achieved {} vs. target {}", @@ -425,7 +432,8 @@ impl InnerService { } } - let new_block_protocol = BlockTemplateProtocol::new(&mut grpc_client, self.config.clone()).await?; + let new_block_protocol = + BlockTemplateProtocol::new(&mut grpc_client, self.config.clone(), self.consensus_manager.clone()).await?; let seed_hash = FixedByteArray::from_hex(&monerod_resp["result"]["seed_hash"].to_string().replace('\"', "")) .map_err(|err| MmProxyError::InvalidMonerodResponse(format!("seed hash hex is invalid: {}", err)))?; @@ -472,7 +480,10 @@ impl InnerService { ); self.block_templates - .save(mining_hash, final_block_template_data.template) + .save( + final_block_template_data.aux_chain_mr, + final_block_template_data.template, + ) .await; debug!(target: LOG_TARGET, "Returning template result: {}", monerod_resp); diff --git a/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs b/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs index 74a97556e7..2966bce7d9 100644 --- a/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs +++ b/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs @@ -87,7 +87,7 @@ pub async fn start_merge_miner(cli: Cli) -> Result<(), anyhow::Error> { base_node_client, BlockTemplateRepository::new(), randomx_factory, - ); + )?; let service = make_service_fn(|_conn| future::ready(Result::<_, Infallible>::Ok(randomx_service.clone()))); match Server::try_bind(&listen_addr) { diff --git a/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs b/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs index be3623ccbc..8077c6db73 100644 --- a/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs +++ b/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs @@ -501,7 +501,7 @@ where B: BlockchainBackend + 'static async fn check_min_block_difficulty(&self, new_block: &NewBlock) -> Result<(), CommsInterfaceError> { let constants = self.consensus_manager.consensus_constants(new_block.header.height); - let gen_hash = self.consensus_manager.get_genesis_block().hash().clone(); + let gen_hash = *self.consensus_manager.get_genesis_block().hash(); let mut min_difficulty = constants.min_pow_difficulty(new_block.header.pow.pow_algo); let mut header = self.blockchain_db.fetch_last_chain_header().await?; loop { diff --git a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs index c3f115ee68..4608fd50c6 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs @@ -146,26 +146,30 @@ fn check_aux_chains( gen_hash: &FixedHash, ) -> bool { let merkle_tree_params = MerkleTreeParameters::from_varint(merge_mining_params); + if merkle_tree_params.number_of_chains == 0 { + return false; + } let hash_position = U256::from_little_endian( &Sha256::new() .chain_update(gen_hash) .chain_update(merkle_tree_params.aux_nonce.to_le_bytes()) - .chain_update((109 as u8).to_le_bytes()) - .finalize() - .to_vec(), + .chain_update((109_u8).to_le_bytes()) + .finalize(), ) .low_u32() % - merkle_tree_params.number_of_chains as u32; - if hash_position != monero_data.aux_chain_merkle_proof.branch().len() as u32 { + u32::from(merkle_tree_params.number_of_chains); + let t_hash = monero::Hash::from_slice(tari_hash.as_slice()); + let (merkle_root, pos) = monero_data.aux_chain_merkle_proof.calculate_root_with_pos(&t_hash); + if hash_position != pos+1 { return false; } - let t_hash = monero::Hash::from_slice(tari_hash.as_slice()); - let merkle_root = monero_data.aux_chain_merkle_proof.calculate_root(&t_hash); + + merkle_root == *aux_chain_merkle_root } /// Extracts the Monero block hash from the coinbase transaction's extra field -pub fn extract_tari_hash_from_block(monero: &monero::Block) -> Result, MergeMineError> { +pub fn extract_aux_merkle_root_from_block(monero: &monero::Block) -> Result, MergeMineError> { // When we extract the merge mining hash, we do not care if the extra field can be parsed without error. let extra_field = parse_extra_field_truncate_on_error(&monero.miner_tx.prefix.extra); @@ -261,9 +265,11 @@ pub fn create_ordered_transaction_hashes_from_block(block: &monero::Block) -> Ve } /// Inserts merge mining hash into a Monero block -pub fn insert_merge_mining_tag_into_block>( +pub fn insert_merge_mining_tag_and_aux_chain_merkle_root_into_block>( block: &mut monero::Block, hash: T, + aux_number: u8, + aux_nonce: u32, ) -> Result<(), MergeMineError> { if hash.as_ref().len() != monero::Hash::len_bytes() { return Err(MergeMineError::HashingError(format!( @@ -290,7 +296,13 @@ pub fn insert_merge_mining_tag_into_block>( // To circumvent this, we create a new extra field by appending the original extra field to the merge mining field // instead. let hash = monero::Hash::from_slice(hash.as_ref()); - extra_field.0.insert(0, SubField::MergeMining(Some(VarInt(0)), hash)); + let mt_params = MerkleTreeParameters { + number_of_chains: aux_number, + aux_nonce, + }; + extra_field + .0 + .insert(0, SubField::MergeMining(Some(mt_params.to_varint()), hash)); block.miner_tx.prefix.extra = extra_field.into(); Ok(()) 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 a51fb03e6f..8e268caf15 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 @@ -192,23 +192,30 @@ impl MerkleProof { } /// Calculates the merkle root hash from the provide Monero hash - pub fn calculate_root(&self, hash: &Hash) -> Hash { + self.calculate_root_with_pos(hash).0 + } + + pub fn calculate_root_with_pos(&self, hash: &Hash) -> (Hash, u32) { if self.branch.is_empty() { - return *hash; + return (*hash, 0); } let mut root = *hash; let depth = self.branch.len(); + let mut pos = 0; + let mut multiplier = 1; for d in 0..depth { if (self.path_bitmap >> (depth - d - 1)) & 1 > 0 { root = cn_fast_hash2(&self.branch[d], &root); } else { root = cn_fast_hash2(&root, &self.branch[d]); + pos += multiplier; } + multiplier *= 2; } - root + (root, pos) } } diff --git a/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs b/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs index 1def68b717..a932c260bf 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs @@ -21,7 +21,7 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::cmp::min; - +use std::convert::TryFrom; use monero::VarInt; pub struct MerkleTreeParameters { @@ -39,51 +39,89 @@ impl MerkleTreeParameters { aux_nonce, } } + + pub fn to_varint(&self) -> VarInt { + let size = u8::try_from(self.number_of_chains.leading_zeros()).expect("This cant fail, u8 can only have 8 leading 0's which will fit in 255"); + let mut bits = encode_bits(8 - size); + let mut n = encode_aux_chain_count(self.number_of_chains, 8 - size); + let mut nonce = encode_aux_nonce(self.aux_nonce); + bits.append(&mut n); + bits.append(&mut nonce); + let num: u64 = bits.iter().fold(0, |result, &bit| (result << 1) ^ u64::from(bit)); + VarInt(num) + } } -pub fn get_decode_bits(num: u64) -> u8 { +fn get_decode_bits(num: u64) -> u8 { let bits_num: Vec = (61..=63).rev().map(|n| ((num >> n) & 1) as u8).collect(); bits_num.iter().fold(0, |result, &bit| (result << 1) ^ bit) } -pub fn get_aux_chain_count(num: u64, bits: u8) -> u8 { +fn encode_bits(num: u8) -> Vec { + (0..=2).rev().map(|n| (num >> n) & 1).collect() +} + +fn get_aux_chain_count(num: u64, bits: u8) -> u8 { let start = 60 - min(8, bits) + 1; let bits_num: Vec = (start..=60).rev().map(|n| ((num >> n) & 1) as u8).collect(); bits_num.iter().fold(0, |result, &bit| (result << 1) ^ bit) } -pub fn get_aux_nonce(num: u64, bits: u8) -> u32 { - let start = 60 - min(8, bits as u32) + 1 - 32; - let end = 60 - min(8, bits as u32); +fn encode_aux_chain_count(num: u8, bit_length: u8) -> Vec { + (0..bit_length).rev().map(|n| (num >> n) & 1).collect() +} + +fn get_aux_nonce(num: u64, bits: u8) -> u32 { + let start = 60 - min(8, u32::from(bits)) + 1 - 32; + let end = 60 - min(8, u32::from(bits)); let bits_num: Vec = (start..=end).rev().map(|n| ((num >> n) & 1) as u32).collect(); bits_num.iter().fold(0, |result, &bit| (result << 1) ^ bit) } +fn encode_aux_nonce(num: u32) -> Vec { + (0..=31).rev().map(|n| ((num >> n) & 1) as u8).collect() +} + #[cfg(test)] mod test { use crate::proof_of_work::monero_rx::merkle_tree_parameters::{ + encode_aux_chain_count, + encode_aux_nonce, + encode_bits, get_aux_chain_count, get_aux_nonce, get_decode_bits, }; #[test] - fn get_decode_bits_test() { + fn en_decode_bits_test() { let num = 0b1100000000000000000000000000000000000000000000000000000000000101; let bit = get_decode_bits(num); assert_eq!(bit, 6); + let bits = encode_bits(6); + let array = vec![1, 1, 0]; + assert_eq!(bits, array); let num = 0b0100000000000000000000000000000000000000000000000000000000000101; let bit = get_decode_bits(num); assert_eq!(bit, 2); + let bits = encode_bits(2); + let array = vec![0, 1, 0]; + assert_eq!(bits, array); let num = 0b1110000000000000000000000000000000000000000000000000000000000101; let bit = get_decode_bits(num); assert_eq!(bit, 7); + let bits = encode_bits(7); + let array = vec![1, 1, 1]; + assert_eq!(bits, array); let num = 0b0011000000000000000000000000000000000000000000000000000000000101; let bit = get_decode_bits(num); assert_eq!(bit, 1); + let bits = encode_bits(1); + let array = vec![0, 0, 1]; + assert_eq!(bits, array); } #[test] @@ -91,34 +129,58 @@ mod test { let num = 0b1101111111100000000000000000000000000000000000000000000000000101; let aux_number = get_aux_chain_count(num, 8); assert_eq!(aux_number, 255); + let bits = encode_aux_chain_count(255, 8); + let array = vec![1, 1, 1, 1, 1, 1, 1, 1]; + assert_eq!(bits, array); let num = 0b1100000000100000000000000000000000000000000000000000000000000101; let aux_number = get_aux_chain_count(num, 8); assert_eq!(aux_number, 1); + let bits = encode_aux_chain_count(1, 8); + let array = vec![0, 0, 0, 0, 0, 0, 0, 1]; + assert_eq!(bits, array); let num = 0b1100000000000000000000000000000000000000000000000000000000000101; let aux_number = get_aux_chain_count(num, 8); assert_eq!(aux_number, 0); + let bits = encode_aux_chain_count(0, 8); + let array = vec![0, 0, 0, 0, 0, 0, 0, 0]; + assert_eq!(bits, array); let num = 0b1100111000000000000000000000000000000000000000000000000000000101; let aux_number = get_aux_chain_count(num, 8); assert_eq!(aux_number, 112); + let bits = encode_aux_chain_count(112, 8); + let array = vec![0, 1, 1, 1, 0, 0, 0, 0]; + assert_eq!(bits, array); let num = 0b1100000100000000000000000000000000000000000000000000000000000101; let aux_number = get_aux_chain_count(num, 8); assert_eq!(aux_number, 8); + let bits = encode_aux_chain_count(8, 8); + let array = vec![0, 0, 0, 0, 1, 0, 0, 0]; + assert_eq!(bits, array); let num = 0b1100000001000000000000000000000000000000000000000000000000000101; let aux_number = get_aux_chain_count(num, 7); assert_eq!(aux_number, 1); + let bits = encode_aux_chain_count(1, 7); + let array = vec![0, 0, 0, 0, 0, 0, 1]; + assert_eq!(bits, array); let num = 0b1100000010000000000000000000000000000000000000000000000000000101; let aux_number = get_aux_chain_count(num, 6); assert_eq!(aux_number, 1); + let bits = encode_aux_chain_count(1, 6); + let array = vec![0, 0, 0, 0, 0, 1]; + assert_eq!(bits, array); let num = 0b1100000100000000000000000000000000000000000000000000000000000101; let aux_number = get_aux_chain_count(num, 5); assert_eq!(aux_number, 1); + let bits = encode_aux_chain_count(1, 5); + let array = vec![0, 0, 0, 0, 1]; + assert_eq!(bits, array); let num = 0b1100000110000000000000000000000000000000000000000000000000000101; let aux_number = get_aux_chain_count(num, 5); @@ -127,6 +189,9 @@ mod test { let num = 0b1111000110000000000000000000000000000000000000000000000000000101; let aux_number = get_aux_chain_count(num, 1); assert_eq!(aux_number, 1); + let bits = encode_aux_chain_count(1, 1); + let array = vec![1]; + assert_eq!(bits, array); } #[test] @@ -134,10 +199,20 @@ mod test { let num = 0b1100000000110000000000000000000000000000000000000000000000000101; let aux_number = get_aux_nonce(num, 8); assert_eq!(aux_number, 2147483648); + let bits = encode_aux_nonce(2147483648); + let array = vec![ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + assert_eq!(bits, array); let num = 0b1100000000011111111111111111111111111111111000000000000000000101; let aux_number = get_aux_nonce(num, 8); assert_eq!(aux_number, u32::MAX); + let bits = encode_aux_nonce(u32::MAX); + let array = vec![ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + ]; + assert_eq!(bits, array); let num = 0b1100000000111111111111111111111111111111110000000000000000000101; let aux_number = get_aux_nonce(num, 7); @@ -150,5 +225,19 @@ mod test { let num = 0b1100000000100000000000000000000000000000001000000000000000000101; let aux_number = get_aux_nonce(num, 8); assert_eq!(aux_number, 1); + let bits = encode_aux_nonce(1); + let array = vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + ]; + assert_eq!(bits, array); + + let num = 0b1100000000100000000000000000000000000000000000000000000000000101; + let aux_number = get_aux_nonce(num, 8); + assert_eq!(aux_number, 0); + let bits = encode_aux_nonce(0); + let array = vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + assert_eq!(bits, array); } } diff --git a/base_layer/core/src/proof_of_work/monero_rx/mod.rs b/base_layer/core/src/proof_of_work/monero_rx/mod.rs index 773681847a..7d29264add 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/mod.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/mod.rs @@ -28,8 +28,8 @@ pub use helpers::{ create_blockhashing_blob_from_block, create_ordered_transaction_hashes_from_block, deserialize_monero_block_from_hex, - extract_tari_hash_from_block, - insert_merge_mining_tag_into_block, + extract_aux_merkle_root_from_block, + insert_merge_mining_tag_and_aux_chain_merkle_root_into_block, randomx_difficulty, serialize_monero_block_to_hex, verify_header, 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 47a1f43a22..d339d40aaa 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 @@ -65,8 +65,8 @@ impl BorshSerialize for MoneroPowData { BorshSerialize::serialize(&self.transaction_count, writer)?; self.merkle_root.consensus_encode(writer)?; BorshSerialize::serialize(&self.coinbase_merkle_proof, writer)?; - BorshSerialize::serialize(&self.aux_chain_merkle_proof, writer)?; self.coinbase_tx.consensus_encode(writer)?; + BorshSerialize::serialize(&self.aux_chain_merkle_proof, writer)?; Ok(()) } } diff --git a/base_layer/core/src/validation/difficulty_calculator.rs b/base_layer/core/src/validation/difficulty_calculator.rs index 772f1630d8..6bdf7435a7 100644 --- a/base_layer/core/src/validation/difficulty_calculator.rs +++ b/base_layer/core/src/validation/difficulty_calculator.rs @@ -51,7 +51,7 @@ impl DifficultyCalculator { constants.min_pow_difficulty(block_header.pow.pow_algo), constants.max_pow_difficulty(block_header.pow.pow_algo), ); - let gen_hash = self.rules.get_genesis_block().hash().clone(); + let gen_hash = *self.rules.get_genesis_block().hash(); let achieved_target = check_target_difficulty(block_header, target, &self.randomx_factory, &gen_hash)?; Ok(achieved_target) diff --git a/base_layer/core/src/validation/header/header_full_validator.rs b/base_layer/core/src/validation/header/header_full_validator.rs index d091edb7ea..3f99583b15 100644 --- a/base_layer/core/src/validation/header/header_full_validator.rs +++ b/base_layer/core/src/validation/header/header_full_validator.rs @@ -77,7 +77,7 @@ impl HeaderChainLinkedValidator for HeaderFullValidator check_timestamp_ftl(header, &self.rules)?; check_pow_data(header, &self.rules, db)?; - let gen_hash = self.rules.get_genesis_block().hash().clone(); + let gen_hash = *self.rules.get_genesis_block().hash(); let achieved_target = if let Some(target) = target_difficulty { check_target_difficulty(header, target, &self.difficulty_calculator.randomx_factory, &gen_hash)? diff --git a/base_layer/core/tests/tests/block_validation.rs b/base_layer/core/tests/tests/block_validation.rs index 34669c6d06..b7c08af196 100644 --- a/base_layer/core/tests/tests/block_validation.rs +++ b/base_layer/core/tests/tests/block_validation.rs @@ -118,6 +118,7 @@ async fn test_monero_blocks() { .add_consensus_constants(cc) .build() .unwrap(); + let gen_hash = *cm.get_genesis_block().hash(); let difficulty_calculator = DifficultyCalculator::new(cm.clone(), RandomXFactory::default()); let header_validator = HeaderFullValidator::new(cm.clone(), difficulty_calculator); let db = create_store_with_consensus_and_validators( @@ -175,7 +176,7 @@ async fn test_monero_blocks() { block_3.header.nonce = 1; let hash2 = block_3.hash(); assert_ne!(hash1, hash2); - assert!(verify_header(&block_3.header).is_ok()); + assert!(verify_header(&block_3.header, &gen_hash).is_ok()); match db.add_block(Arc::new(block_3.clone())) { Err(ChainStorageError::ValidationError { source: ValidationError::BlockHeaderError(BlockHeaderValidationError::InvalidNonce), @@ -198,11 +199,13 @@ fn add_monero_data(tblock: &mut Block, seed_key: &str) { .to_string(); let bytes = hex::decode(blocktemplate_blob).unwrap(); let mut mblock = monero_rx::deserialize::(&bytes[..]).unwrap(); - let hash = tblock.header.merge_mining_hash(); - monero_rx::insert_merge_mining_tag_into_block(&mut mblock, hash).unwrap(); + let hash = monero::Hash::from_slice(tblock.header.merge_mining_hash().as_slice()); + monero_rx::insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&mut mblock, hash,1, 0).unwrap(); let hashes = monero_rx::create_ordered_transaction_hashes_from_block(&mblock); let merkle_root = monero_rx::tree_hash(&hashes).unwrap(); let coinbase_merkle_proof = monero_rx::create_merkle_proof(&hashes, &hashes[0]).unwrap(); + let aux_hashes = vec![hash]; + let aux_chain_merkle_proof = monero_rx::create_merkle_proof(&aux_hashes, &aux_hashes[0]).unwrap(); #[allow(clippy::cast_possible_truncation)] let monero_data = MoneroPowData { header: mblock.header, @@ -211,6 +214,7 @@ fn add_monero_data(tblock: &mut Block, seed_key: &str) { merkle_root, coinbase_merkle_proof, coinbase_tx: mblock.miner_tx, + aux_chain_merkle_proof, }; let mut serialized = Vec::new(); BorshSerialize::serialize(&monero_data, &mut serialized).unwrap(); From e08592b483597ac0ca3edb1344132b11f9c52770 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Thu, 23 Nov 2023 16:44:48 +0200 Subject: [PATCH 04/14] fix tests --- .../core/src/proof_of_work/monero_rx/helpers.rs | 6 +----- .../monero_rx/merkle_tree_parameters.rs | 12 +++++++++--- base_layer/core/tests/tests/block_validation.rs | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs index 4608fd50c6..7a5bf2310f 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs @@ -119,9 +119,6 @@ pub fn verify_header(header: &BlockHeader, gen_hash: &FixedHash) -> Result VarInt { - let size = u8::try_from(self.number_of_chains.leading_zeros()).expect("This cant fail, u8 can only have 8 leading 0's which will fit in 255"); + let size = u8::try_from(self.number_of_chains.leading_zeros()) + .expect("This cant fail, u8 can only have 8 leading 0's which will fit in 255"); let mut bits = encode_bits(8 - size); let mut n = encode_aux_chain_count(self.number_of_chains, 8 - size); let mut nonce = encode_aux_nonce(self.aux_nonce); bits.append(&mut n); bits.append(&mut nonce); + if bits.len() < 64 { + let mut missing_zeroes = vec![0; 64 - bits.len()]; + bits.append(&mut missing_zeroes); + } let num: u64 = bits.iter().fold(0, |result, &bit| (result << 1) ^ u64::from(bit)); VarInt(num) } diff --git a/base_layer/core/tests/tests/block_validation.rs b/base_layer/core/tests/tests/block_validation.rs index b7c08af196..013512c73b 100644 --- a/base_layer/core/tests/tests/block_validation.rs +++ b/base_layer/core/tests/tests/block_validation.rs @@ -200,7 +200,7 @@ fn add_monero_data(tblock: &mut Block, seed_key: &str) { let bytes = hex::decode(blocktemplate_blob).unwrap(); let mut mblock = monero_rx::deserialize::(&bytes[..]).unwrap(); let hash = monero::Hash::from_slice(tblock.header.merge_mining_hash().as_slice()); - monero_rx::insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&mut mblock, hash,1, 0).unwrap(); + monero_rx::insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&mut mblock, hash, 1, 0).unwrap(); let hashes = monero_rx::create_ordered_transaction_hashes_from_block(&mblock); let merkle_root = monero_rx::tree_hash(&hashes).unwrap(); let coinbase_merkle_proof = monero_rx::create_merkle_proof(&hashes, &hashes[0]).unwrap(); From ffa0fcf955d38380f162cba8e58c5ffd0f4cedb9 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Thu, 23 Nov 2023 19:54:00 +0200 Subject: [PATCH 05/14] fix tests --- .../src/proof_of_work/monero_rx/helpers.rs | 1318 +++++++++-------- 1 file changed, 664 insertions(+), 654 deletions(-) diff --git a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs index 7a5bf2310f..b9e057614a 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs @@ -317,657 +317,667 @@ pub fn create_block_hashing_blob( blockhashing_blob } -// #[cfg(test)] -// mod test { -// use std::convert::{TryFrom, TryInto}; -// -// use borsh::BorshSerialize; -// use monero::{ -// blockdata::transaction::{ExtraField, TxOutTarget}, -// consensus::deserialize, -// cryptonote::hash::Hashable, -// util::ringct::{RctSig, RctSigBase, RctType}, -// Hash, -// PublicKey, -// Transaction, -// TransactionPrefix, -// TxIn, -// TxOut, -// }; -// use tari_common_types::types::FixedHash; -// use tari_test_utils::unpack_enum; -// use tari_utilities::{ -// epoch_time::EpochTime, -// hex::{from_hex, Hex}, -// ByteArray, -// }; -// -// use super::*; -// use crate::proof_of_work::{monero_rx::fixed_array::FixedByteArray, PowAlgorithm, ProofOfWork}; -// -// // This tests checks the hash of monero-rs -// #[test] -// fn test_monero_rs_miner_tx_hash() { -// let tx = "f8ad7c58e6fce1792dd78d764ce88a11db0e3c3bb484d868ae05a7321fb6c6b0"; -// -// let pk_extra = vec![ -// 179, 155, 220, 223, 213, 23, 81, 160, 95, 232, 87, 102, 151, 63, 70, 249, 139, 40, 110, 16, 51, 193, 175, -// 208, 38, 120, 65, 191, 155, 139, 1, 4, -// ]; -// let transaction = Transaction { -// prefix: TransactionPrefix { -// version: VarInt(2), -// unlock_time: VarInt(2143845), -// inputs: vec![TxIn::Gen { -// height: VarInt(2143785), -// }], -// outputs: vec![TxOut { -// amount: VarInt(1550800739964), -// target: TxOutTarget::ToKey { -// key: hex::decode("e2e19d8badb15e77c8e1f441cf6acd9bcde34a07cae82bbe5ff9629bf88e6e81") -// .unwrap() -// .as_slice() -// .try_into() -// .unwrap(), -// }, -// }], -// extra: ExtraField(vec![ -// SubField::TxPublicKey(PublicKey::from_slice(pk_extra.as_slice()).unwrap()), -// SubField::Nonce(vec![196, 37, 4, 0, 27, 37, 187, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0]), -// ]) -// .into(), -// }, -// signatures: vec![], -// rct_signatures: RctSig { -// sig: Option::from(RctSigBase { -// rct_type: RctType::Null, -// txn_fee: Default::default(), -// pseudo_outs: vec![], -// ecdh_info: vec![], -// out_pk: vec![], -// }), -// p: None, -// }, -// }; -// assert_eq!( -// tx.as_bytes().to_vec(), -// hex::encode(transaction.hash().0.to_vec()).as_bytes().to_vec() -// ); -// let hex = hex::encode(consensus::serialize::(&transaction)); -// deserialize::(&hex::decode(hex).unwrap()).unwrap(); -// } -// -// // This tests checks the blockhashing blob of monero-rs -// #[test] -// fn test_monero_rs_block_serialize() { -// // block with only the miner tx and no other transactions -// let hex = -// "0c0c94debaf805beb3489c722a285c092a32e7c6893abfc7d069699c8326fc3445a749c5276b6200000000029b892201ffdf882201b699d4c8b1ec020223df524af2a2ef5f870adb6e1ceb03a475c39f8b9ef76aa50b46ddd2a18349402b012839bfa19b7524ec7488917714c216ca254b38ed0424ca65ae828a7c006aeaf10208f5316a7f6b99cca60000" -// ; // blockhashing blob for above block as accepted by monero -// let -// hex_blockhash_blob=" -// 0c0c94debaf805beb3489c722a285c092a32e7c6893abfc7d069699c8326fc3445a749c5276b6200000000602d0d4710e2c2d38da0cce097accdf5dc18b1d34323880c1aae90ab8f6be6e201" -// ; let bytes = hex::decode(hex).unwrap(); -// let block = deserialize::(&bytes[..]).unwrap(); -// let header = consensus::serialize::(&block.header); -// let tx_count = 1 + block.tx_hashes.len() as u64; -// let mut count = consensus::serialize::(&VarInt(tx_count)); -// #[allow(clippy::cast_possible_truncation)] -// let mut hashes = Vec::with_capacity(tx_count as usize); -// hashes.push(block.miner_tx.hash()); -// for item in block.clone().tx_hashes { -// hashes.push(item); -// } -// let root = tree_hash(&hashes).unwrap(); -// let mut encode2 = header; -// encode2.extend_from_slice(root.as_bytes()); -// encode2.append(&mut count); -// assert_eq!(hex::encode(encode2), hex_blockhash_blob); -// let bytes2 = consensus::serialize::(&block); -// assert_eq!(bytes, bytes2); -// let hex2 = hex::encode(bytes2); -// assert_eq!(hex, hex2); -// } -// -// #[test] -// fn test_monero_data() { -// let blocktemplate_blob = -// "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000" -// .to_string(); let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); -// let bytes = hex::decode(blocktemplate_blob).unwrap(); -// let mut block = deserialize::(&bytes[..]).unwrap(); -// let mut block_header = BlockHeader { -// version: 0, -// height: 0, -// prev_hash: FixedHash::zero(), -// timestamp: EpochTime::now(), -// output_mr: FixedHash::zero(), -// output_smt_size: 0, -// kernel_mr: FixedHash::zero(), -// kernel_mmr_size: 0, -// input_mr: FixedHash::zero(), -// total_kernel_offset: Default::default(), -// total_script_offset: Default::default(), -// nonce: 0, -// pow: ProofOfWork::default(), -// validator_node_mr: FixedHash::zero(), -// validator_node_size: 0, -// }; -// let hash = block_header.merge_mining_hash(); -// insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); -// let hashes = create_ordered_transaction_hashes_from_block(&block); -// assert_eq!(hashes.len(), block.tx_hashes.len() + 1); -// let root = tree_hash(&hashes).unwrap(); -// let coinbase_merkle_proof = create_merkle_proof(&hashes, &hashes[0]).unwrap(); -// -// let monero_data = MoneroPowData { -// header: block.header, -// randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), -// transaction_count: u16::try_from(hashes.len()).unwrap(), -// merkle_root: root, -// coinbase_merkle_proof, -// coinbase_tx: block.miner_tx, -// }; -// let mut serialized = Vec::new(); -// monero_data.serialize(&mut serialized).unwrap(); -// let pow = ProofOfWork { -// pow_algo: PowAlgorithm::RandomX, -// pow_data: serialized, -// }; -// block_header.pow = pow; -// MoneroPowData::from_header(&block_header).unwrap(); -// } -// -// #[test] -// fn test_input_blob() { -// let blocktemplate_blob = -// "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000" -// .to_string(); let bytes = hex::decode(blocktemplate_blob).unwrap(); -// let block = deserialize::(&bytes[..]).unwrap(); -// let input_blob = create_blockhashing_blob_from_block(&block).unwrap(); -// assert_eq!(input_blob, -// "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000058b030b6800d433bbcb2b560afe2a08e4dc152fa77ead96d37aaf14897d3c09601" -// ); } -// -// #[test] -// fn test_append_mm_tag() { -// let blocktemplate_blob = -// "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000" -// .to_string(); let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); -// let bytes = hex::decode(blocktemplate_blob).unwrap(); -// let mut block = deserialize::(&bytes[..]).unwrap(); -// let mut block_header = BlockHeader { -// version: 0, -// height: 0, -// prev_hash: FixedHash::zero(), -// timestamp: EpochTime::now(), -// output_mr: FixedHash::zero(), -// output_smt_size: 0, -// kernel_mr: FixedHash::zero(), -// kernel_mmr_size: 0, -// input_mr: FixedHash::zero(), -// total_kernel_offset: Default::default(), -// total_script_offset: Default::default(), -// nonce: 0, -// pow: ProofOfWork::default(), -// validator_node_mr: FixedHash::zero(), -// validator_node_size: 0, -// }; -// let hash = block_header.merge_mining_hash(); -// insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); -// let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); -// let mut hashes = Vec::with_capacity(count as usize); -// hashes.push(block.miner_tx.hash()); -// // Note: tx_hashes is empty, so |hashes| == 1 -// for item in block.clone().tx_hashes { -// hashes.push(item); -// } -// let root = tree_hash(&hashes).unwrap(); -// assert_eq!(root, hashes[0]); -// let coinbase_merkle_proof = create_merkle_proof(&hashes,&hashes[0]).unwrap(); -// let monero_data = MoneroPowData { -// header: block.header, -// randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), -// transaction_count: count, -// merkle_root: root, -// coinbase_merkle_proof, -// coinbase_tx: block.miner_tx, -// }; -// let mut serialized = Vec::new(); -// monero_data.serialize(&mut serialized).unwrap(); -// let pow = ProofOfWork { -// pow_algo: PowAlgorithm::RandomX, -// pow_data: serialized, -// }; -// block_header.pow = pow; -// verify_header(&block_header).unwrap(); -// } -// -// #[test] -// fn test_append_mm_tag_no_tag() { -// let blocktemplate_blob = -// "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000" -// .to_string(); let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); -// let bytes = hex::decode(blocktemplate_blob).unwrap(); -// let block = deserialize::(&bytes[..]).unwrap(); -// let mut block_header = BlockHeader { -// version: 0, -// height: 0, -// prev_hash: FixedHash::zero(), -// timestamp: EpochTime::now(), -// output_mr: FixedHash::zero(), -// output_smt_size: 0, -// kernel_mr: FixedHash::zero(), -// kernel_mmr_size: 0, -// input_mr: FixedHash::zero(), -// total_kernel_offset: Default::default(), -// total_script_offset: Default::default(), -// nonce: 0, -// pow: ProofOfWork::default(), -// validator_node_mr: FixedHash::zero(), -// validator_node_size: 0, -// }; -// let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); -// let mut hashes = Vec::with_capacity(count as usize); -// hashes.push(block.miner_tx.hash()); -// for item in block.clone().tx_hashes { -// hashes.push(item); -// } -// let root = tree_hash(&hashes).unwrap(); -// let coinbase_merkle_proof = create_merkle_proof(&hashes,&hashes[0]).unwrap(); -// let monero_data = MoneroPowData { -// header: block.header, -// randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), -// transaction_count: count, -// merkle_root: root, -// coinbase_merkle_proof, -// coinbase_tx: block.miner_tx, -// }; -// -// let mut serialized = Vec::new(); -// monero_data.serialize(&mut serialized).unwrap(); -// let pow = ProofOfWork { -// pow_algo: PowAlgorithm::RandomX, -// pow_data: serialized, -// }; -// block_header.pow = pow; -// let err = verify_header(&block_header).unwrap_err(); -// unpack_enum!(MergeMineError::ValidationError(details) = err); -// assert!(details.contains("Expected merge mining tag was not found in Monero coinbase transaction")); -// } -// -// #[test] -// fn test_append_mm_tag_wrong_hash() { -// let blocktemplate_blob = -// "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000" -// .to_string(); let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); -// let bytes = hex::decode(blocktemplate_blob).unwrap(); -// let mut block = deserialize::(&bytes[..]).unwrap(); -// let mut block_header = BlockHeader { -// version: 0, -// height: 0, -// prev_hash: FixedHash::zero(), -// timestamp: EpochTime::now(), -// output_mr: FixedHash::zero(), -// output_smt_size: 0, -// kernel_mr: FixedHash::zero(), -// kernel_mmr_size: 0, -// input_mr: FixedHash::zero(), -// total_kernel_offset: Default::default(), -// total_script_offset: Default::default(), -// nonce: 0, -// pow: ProofOfWork::default(), -// validator_node_mr: FixedHash::zero(), -// validator_node_size: 0, -// }; -// let hash = Hash::null(); -// insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); -// let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); -// let mut hashes = Vec::with_capacity(count as usize); -// let mut proof = Vec::with_capacity(count as usize); -// hashes.push(block.miner_tx.hash()); -// proof.push(block.miner_tx.hash()); -// for item in block.clone().tx_hashes { -// hashes.push(item); -// proof.push(item); -// } -// let root = tree_hash(&hashes).unwrap(); -// let coinbase_merkle_proof = create_merkle_proof(&hashes, &hashes[0]).unwrap(); -// let monero_data = MoneroPowData { -// header: block.header, -// randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), -// transaction_count: count, -// merkle_root: root, -// coinbase_merkle_proof, -// coinbase_tx: block.miner_tx, -// }; -// let mut serialized = Vec::new(); -// monero_data.serialize(&mut serialized).unwrap(); -// let pow = ProofOfWork { -// pow_algo: PowAlgorithm::RandomX, -// pow_data: serialized, -// }; -// block_header.pow = pow; -// let err = verify_header(&block_header).unwrap_err(); -// unpack_enum!(MergeMineError::ValidationError(details) = err); -// assert!(details.contains("Expected merge mining tag was not found in Monero coinbase transaction")); -// } -// -// #[test] -// fn test_duplicate_append_mm_tag() { -// let blocktemplate_blob = -// "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000" -// .to_string(); let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); -// let bytes = hex::decode(blocktemplate_blob).unwrap(); -// let mut block = deserialize::(&bytes[..]).unwrap(); -// let mut block_header = BlockHeader { -// version: 0, -// height: 0, -// prev_hash: FixedHash::zero(), -// timestamp: EpochTime::now(), -// output_mr: FixedHash::zero(), -// output_smt_size: 0, -// kernel_mr: FixedHash::zero(), -// kernel_mmr_size: 0, -// input_mr: FixedHash::zero(), -// total_kernel_offset: Default::default(), -// total_script_offset: Default::default(), -// nonce: 0, -// pow: ProofOfWork::default(), -// validator_node_mr: FixedHash::zero(), -// validator_node_size: 0, -// }; -// let hash = block_header.merge_mining_hash(); -// insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); -// #[allow(clippy::redundant_clone)] -// let mut block_header2 = block_header.clone(); -// block_header2.version = 1; -// let hash2 = block_header2.merge_mining_hash(); -// assert!(extract_tari_hash_from_block(&block).is_ok()); -// -// // Try via the API - this will fail because more than one merge mining tag is not allowed -// assert!(insert_merge_mining_tag_into_block(&mut block, hash2).is_err()); -// -// // Now bypass the API - this will effectively allow us to insert more than one merge mining tag, -// // like trying to sneek it in. Later on, when we call `verify_header(&block_header)`, it should fail. -// let mut extra_field = ExtraField::try_parse(&block.miner_tx.prefix.extra).unwrap(); -// let hash = monero::Hash::from_slice(hash.as_ref()); -// extra_field.0.insert(0, SubField::MergeMining(Some(VarInt(0)), hash)); -// block.miner_tx.prefix.extra = extra_field.into(); -// -// // Trying to extract the Tari hash will fail because there are more than one merge mining tag -// let err = extract_tari_hash_from_block(&block).unwrap_err(); -// unpack_enum!(MergeMineError::ValidationError(details) = err); -// assert!(details.contains("More than one merge mining tag found in coinbase")); -// -// let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); -// let mut hashes = Vec::with_capacity(count as usize); -// hashes.push(block.miner_tx.hash()); -// // Note: tx_hashes is empty, so |hashes| == 1 -// for item in block.clone().tx_hashes { -// hashes.push(item); -// } -// let root = tree_hash(&hashes).unwrap(); -// assert_eq!(root, hashes[0]); -// let coinbase_merkle_proof = create_merkle_proof(&hashes, &hashes[0]).unwrap(); -// let monero_data = MoneroPowData { -// header: block.header, -// randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), -// transaction_count: count, -// merkle_root: root, -// coinbase_merkle_proof, -// coinbase_tx: block.miner_tx, -// }; -// let mut serialized = Vec::new(); -// monero_data.serialize(&mut serialized).unwrap(); -// let pow = ProofOfWork { -// pow_algo: PowAlgorithm::RandomX, -// pow_data: serialized, -// }; -// block_header.pow = pow; -// -// // Header verification will fail because there are more than one merge mining tag -// let err = verify_header(&block_header).unwrap_err(); -// unpack_enum!(MergeMineError::ValidationError(details) = err); -// assert!(details.contains("More than one merge mining tag found in coinbase")); -// } -// -// #[test] -// fn test_extra_field_with_parsing_error() { -// let blocktemplate_blob = -// "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000" -// .to_string(); let bytes = hex::decode(blocktemplate_blob).unwrap(); -// let mut block = deserialize::(&bytes[..]).unwrap(); -// let block_header = BlockHeader { -// version: 0, -// height: 0, -// prev_hash: FixedHash::zero(), -// timestamp: EpochTime::now(), -// output_mr: FixedHash::zero(), -// output_smt_size: 0, -// kernel_mr: FixedHash::zero(), -// kernel_mmr_size: 0, -// input_mr: FixedHash::zero(), -// total_kernel_offset: Default::default(), -// total_script_offset: Default::default(), -// nonce: 0, -// pow: ProofOfWork::default(), -// validator_node_mr: FixedHash::zero(), -// validator_node_size: 2, -// }; -// -// // Let us manipulate the extra field to make it invalid -// let mut extra_field_before_parse = ExtraField::try_parse(&block.miner_tx.prefix.extra).unwrap(); -// assert_eq!( -// "ExtraField([TxPublicKey(06225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa4782), Nonce([246, \ -// 58, 168, 109, 46, 133, 127, 7])])", -// &format!("{:?}", extra_field_before_parse) -// ); -// assert!(ExtraField::try_parse(&extra_field_before_parse.clone().into()).is_ok()); -// -// extra_field_before_parse.0.insert(0, SubField::Padding(230)); -// assert_eq!( -// "ExtraField([Padding(230), TxPublicKey(06225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa4782), -// \ Nonce([246, 58, 168, 109, 46, 133, 127, 7])])", -// &format!("{:?}", extra_field_before_parse) -// ); -// assert!(ExtraField::try_parse(&extra_field_before_parse.clone().into()).is_err()); -// -// // Now insert the merge mining tag - this would also clean up the extra field and remove the invalid -// sub-fields let hash = block_header.merge_mining_hash(); -// insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); -// assert!(ExtraField::try_parse(&block.miner_tx.prefix.extra.clone()).is_ok()); -// -// // Verify that the merge mining tag is there -// let extra_field_after_tag = ExtraField::try_parse(&block.miner_tx.prefix.extra.clone()).unwrap(); -// assert_eq!( -// &format!( -// "ExtraField([MergeMining(Some(0), 0x{}), \ -// TxPublicKey(06225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa4782), Nonce([246, 58, 168, -// \ 109, 46, 133, 127, 7])])", -// hex::encode(hash) -// ), -// &format!("{:?}", extra_field_after_tag) -// ); -// } -// -// #[test] -// fn test_verify_header_no_coinbase() { -// let blocktemplate_blob = -// "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000" -// .to_string(); let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); -// let bytes = hex::decode(blocktemplate_blob).unwrap(); -// let mut block = deserialize::(&bytes[..]).unwrap(); -// let mut block_header = BlockHeader { -// version: 0, -// height: 0, -// prev_hash: FixedHash::zero(), -// timestamp: EpochTime::now(), -// output_mr: FixedHash::zero(), -// output_smt_size: 0, -// kernel_mr: FixedHash::zero(), -// kernel_mmr_size: 0, -// input_mr: FixedHash::zero(), -// total_kernel_offset: Default::default(), -// total_script_offset: Default::default(), -// nonce: 0, -// pow: ProofOfWork::default(), -// validator_node_mr: FixedHash::zero(), -// validator_node_size: 0, -// }; -// let hash = block_header.merge_mining_hash(); -// insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); -// let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); -// let mut hashes = Vec::with_capacity(count as usize); -// let mut proof = Vec::with_capacity(count as usize); -// hashes.push(block.miner_tx.hash()); -// proof.push(block.miner_tx.hash()); -// for item in block.clone().tx_hashes { -// hashes.push(item); -// proof.push(item); -// } -// let root = tree_hash(&hashes).unwrap(); -// let coinbase_merkle_proof = create_merkle_proof(&hashes,&hashes[0]).unwrap(); -// let monero_data = MoneroPowData { -// header: block.header, -// randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), -// transaction_count: count, -// merkle_root: root, -// coinbase_merkle_proof, -// coinbase_tx: Default::default(), -// }; -// let mut serialized = Vec::new(); -// monero_data.serialize(&mut serialized).unwrap(); -// let pow = ProofOfWork { -// pow_algo: PowAlgorithm::RandomX, -// pow_data: serialized, -// }; -// block_header.pow = pow; -// let err = verify_header(&block_header).unwrap_err(); -// unpack_enum!(MergeMineError::ValidationError(details) = err); -// assert!(details.contains("Expected merge mining tag was not found in Monero coinbase transaction")); -// } -// -// #[test] -// fn test_verify_header_no_data() { -// let mut block_header = BlockHeader { -// version: 0, -// height: 0, -// prev_hash: FixedHash::zero(), -// timestamp: EpochTime::now(), -// output_mr: FixedHash::zero(), -// output_smt_size: 0, -// kernel_mr: FixedHash::zero(), -// kernel_mmr_size: 0, -// input_mr: FixedHash::zero(), -// total_kernel_offset: Default::default(), -// total_script_offset: Default::default(), -// nonce: 0, -// pow: ProofOfWork::default(), -// validator_node_mr: FixedHash::zero(), -// validator_node_size: 0, -// }; -// let monero_data = MoneroPowData { -// header: Default::default(), -// randomx_key: FixedByteArray::default(), -// transaction_count: 1, -// merkle_root: Default::default(), -// coinbase_merkle_proof: create_merkle_proof(&[Hash::null()], &Hash::null()).unwrap(), -// coinbase_tx: Default::default(), -// }; -// let mut serialized = Vec::new(); -// monero_data.serialize(&mut serialized).unwrap(); -// let pow = ProofOfWork { -// pow_algo: PowAlgorithm::RandomX, -// pow_data: serialized, -// }; -// block_header.pow = pow; -// let err = verify_header(&block_header).unwrap_err(); -// unpack_enum!(MergeMineError::ValidationError(details) = err); -// assert!(details.contains("Expected merge mining tag was not found in Monero coinbase transaction")); -// } -// -// #[test] -// fn test_verify_invalid_root() { -// let blocktemplate_blob = -// "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000" -// .to_string(); let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); -// let bytes = hex::decode(blocktemplate_blob).unwrap(); -// let mut block = deserialize::(&bytes[..]).unwrap(); -// let mut block_header = BlockHeader { -// version: 0, -// height: 0, -// prev_hash: FixedHash::zero(), -// timestamp: EpochTime::now(), -// output_mr: FixedHash::zero(), -// output_smt_size: 0, -// kernel_mr: FixedHash::zero(), -// kernel_mmr_size: 0, -// input_mr: FixedHash::zero(), -// total_kernel_offset: Default::default(), -// total_script_offset: Default::default(), -// nonce: 0, -// pow: ProofOfWork::default(), -// validator_node_mr: FixedHash::zero(), -// validator_node_size: 0, -// }; -// let hash = block_header.merge_mining_hash(); -// insert_merge_mining_tag_into_block(&mut block, hash).unwrap(); -// let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); -// let mut hashes = Vec::with_capacity(count as usize); -// let mut proof = Vec::with_capacity(count as usize); -// hashes.push(block.miner_tx.hash()); -// proof.push(block.miner_tx.hash()); -// for item in block.clone().tx_hashes { -// hashes.push(item); -// proof.push(item); -// } -// -// let coinbase_merkle_proof = create_merkle_proof(&hashes, &hashes[0]).unwrap(); -// let monero_data = MoneroPowData { -// header: block.header, -// randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), -// transaction_count: count, -// merkle_root: Hash::null(), -// coinbase_merkle_proof, -// coinbase_tx: block.miner_tx, -// }; -// let mut serialized = Vec::new(); -// monero_data.serialize(&mut serialized).unwrap(); -// let pow = ProofOfWork { -// pow_algo: PowAlgorithm::RandomX, -// pow_data: serialized, -// }; -// block_header.pow = pow; -// let err = verify_header(&block_header).unwrap_err(); -// unpack_enum!(MergeMineError::InvalidMerkleRoot = err); -// } -// -// #[test] -// fn test_difficulty() { -// // Taken from block: https://stagenet.xmrchain.net/search?value=672576 -// let versions = "0c0c"; -// // Tool for encoding VarInts: -// // https://gchq.github.io/CyberChef/#recipe=VarInt_Encode()To_Hex('Space',0)From_Hex('Auto'/disabled)VarInt_Decode(/disabled)&input=MTYwMTAzMTIwMg -// let timestamp = "a298b7fb05"; // 1601031202 -// let prev_block = "046f4fe371f9acdc27c377f4adee84e93b11f89246a74dd77f1bf0856141da5c"; -// let nonce = "FE394F12"; // 307182078 -// let tx_hash = "77139305ea53cfe95cf7235d2fed6fca477395b019b98060acdbc0f8fb0b8b92"; // miner tx -// let count = "01"; -// -// let input = from_hex(&format!( -// "{}{}{}{}{}{}", -// versions, timestamp, prev_block, nonce, tx_hash, count -// )) -// .unwrap(); -// let key = from_hex("2aca6501719a5c7ab7d4acbc7cc5d277b57ad8c27c6830788c2d5a596308e5b1").unwrap(); -// let rx = RandomXFactory::default(); -// -// let (difficulty, hash) = get_random_x_difficulty(&input, &rx.create(&key).unwrap()).unwrap(); -// assert_eq!( -// hash.to_hex(), -// "f68fbc8cc85bde856cd1323e9f8e6f024483038d728835de2f8c014ff6260000" -// ); -// assert_eq!(difficulty.as_u64(), 430603); -// } -// } +#[cfg(test)] +mod test { + use std::convert::{TryFrom, TryInto}; + + use borsh::BorshSerialize; + use monero::{ + blockdata::transaction::{ExtraField, TxOutTarget}, + consensus::deserialize, + cryptonote::hash::Hashable, + util::ringct::{RctSig, RctSigBase, RctType}, + Hash, + PublicKey, + Transaction, + TransactionPrefix, + TxIn, + TxOut, + }; + use tari_common_types::types::FixedHash; + use tari_test_utils::unpack_enum; + use tari_utilities::{ + epoch_time::EpochTime, + hex::{from_hex, Hex}, + ByteArray, + }; + + use super::*; + use crate::proof_of_work::{monero_rx::fixed_array::FixedByteArray, PowAlgorithm, ProofOfWork}; + + // This tests checks the hash of monero-rs + #[test] + fn test_monero_rs_miner_tx_hash() { + let tx = "f8ad7c58e6fce1792dd78d764ce88a11db0e3c3bb484d868ae05a7321fb6c6b0"; + + let pk_extra = vec![ + 179, 155, 220, 223, 213, 23, 81, 160, 95, 232, 87, 102, 151, 63, 70, 249, 139, 40, 110, 16, 51, 193, 175, + 208, 38, 120, 65, 191, 155, 139, 1, 4, + ]; + let transaction = Transaction { + prefix: TransactionPrefix { + version: VarInt(2), + unlock_time: VarInt(2143845), + inputs: vec![TxIn::Gen { + height: VarInt(2143785), + }], + outputs: vec![TxOut { + amount: VarInt(1550800739964), + target: TxOutTarget::ToKey { + key: hex::decode("e2e19d8badb15e77c8e1f441cf6acd9bcde34a07cae82bbe5ff9629bf88e6e81") + .unwrap() + .as_slice() + .try_into() + .unwrap(), + }, + }], + extra: ExtraField(vec![ + SubField::TxPublicKey(PublicKey::from_slice(pk_extra.as_slice()).unwrap()), + SubField::Nonce(vec![196, 37, 4, 0, 27, 37, 187, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ]) + .into(), + }, + signatures: vec![], + rct_signatures: RctSig { + sig: Option::from(RctSigBase { + rct_type: RctType::Null, + txn_fee: Default::default(), + pseudo_outs: vec![], + ecdh_info: vec![], + out_pk: vec![], + }), + p: None, + }, + }; + assert_eq!( + tx.as_bytes().to_vec(), + hex::encode(transaction.hash().0.to_vec()).as_bytes().to_vec() + ); + let hex = hex::encode(consensus::serialize::(&transaction)); + deserialize::(&hex::decode(hex).unwrap()).unwrap(); + } + + // This tests checks the blockhashing blob of monero-rs + #[test] + fn test_monero_rs_block_serialize() { + // block with only the miner tx and no other transactions + let hex = "0c0c94debaf805beb3489c722a285c092a32e7c6893abfc7d069699c8326fc3445a749c5276b6200000000029b892201ffdf882201b699d4c8b1ec020223df524af2a2ef5f870adb6e1ceb03a475c39f8b9ef76aa50b46ddd2a18349402b012839bfa19b7524ec7488917714c216ca254b38ed0424ca65ae828a7c006aeaf10208f5316a7f6b99cca60000"; + // blockhashing blob for above block as accepted by monero + let hex_blockhash_blob="0c0c94debaf805beb3489c722a285c092a32e7c6893abfc7d069699c8326fc3445a749c5276b6200000000602d0d4710e2c2d38da0cce097accdf5dc18b1d34323880c1aae90ab8f6be6e201"; + let bytes = hex::decode(hex).unwrap(); + let block = deserialize::(&bytes[..]).unwrap(); + let header = consensus::serialize::(&block.header); + let tx_count = 1 + block.tx_hashes.len() as u64; + let mut count = consensus::serialize::(&VarInt(tx_count)); + #[allow(clippy::cast_possible_truncation)] + let mut hashes = Vec::with_capacity(tx_count as usize); + hashes.push(block.miner_tx.hash()); + for item in block.clone().tx_hashes { + hashes.push(item); + } + let root = tree_hash(&hashes).unwrap(); + let mut encode2 = header; + encode2.extend_from_slice(root.as_bytes()); + encode2.append(&mut count); + assert_eq!(hex::encode(encode2), hex_blockhash_blob); + let bytes2 = consensus::serialize::(&block); + assert_eq!(bytes, bytes2); + let hex2 = hex::encode(bytes2); + assert_eq!(hex, hex2); + } + + #[test] + fn test_monero_data() { + let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); + let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); + let bytes = hex::decode(blocktemplate_blob).unwrap(); + let mut block = deserialize::(&bytes[..]).unwrap(); + let mut block_header = BlockHeader { + version: 0, + height: 0, + prev_hash: FixedHash::zero(), + timestamp: EpochTime::now(), + output_mr: FixedHash::zero(), + output_smt_size: 0, + kernel_mr: FixedHash::zero(), + kernel_mmr_size: 0, + input_mr: FixedHash::zero(), + total_kernel_offset: Default::default(), + total_script_offset: Default::default(), + nonce: 0, + pow: ProofOfWork::default(), + validator_node_mr: FixedHash::zero(), + validator_node_size: 0, + }; + let hash = block_header.merge_mining_hash(); + insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&mut block, hash, 1, 0).unwrap(); + let hashes = create_ordered_transaction_hashes_from_block(&block); + assert_eq!(hashes.len(), block.tx_hashes.len() + 1); + let root = tree_hash(&hashes).unwrap(); + let coinbase_merkle_proof = create_merkle_proof(&hashes, &hashes[0]).unwrap(); + let aux_hashes = vec![monero::Hash::from_slice(hash.as_ref())]; + let aux_chain_merkle_proof = create_merkle_proof(&aux_hashes, &aux_hashes[0]).unwrap(); + + let monero_data = MoneroPowData { + header: block.header, + randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), + transaction_count: u16::try_from(hashes.len()).unwrap(), + merkle_root: root, + coinbase_merkle_proof, + coinbase_tx: block.miner_tx, + aux_chain_merkle_proof, + }; + let mut serialized = Vec::new(); + monero_data.serialize(&mut serialized).unwrap(); + let pow = ProofOfWork { + pow_algo: PowAlgorithm::RandomX, + pow_data: serialized, + }; + block_header.pow = pow; + MoneroPowData::from_header(&block_header).unwrap(); + } + + #[test] + fn test_input_blob() { + let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); + let bytes = hex::decode(blocktemplate_blob).unwrap(); + let block = deserialize::(&bytes[..]).unwrap(); + let input_blob = create_blockhashing_blob_from_block(&block).unwrap(); + assert_eq!(input_blob, "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000058b030b6800d433bbcb2b560afe2a08e4dc152fa77ead96d37aaf14897d3c09601"); + } + + #[test] + fn test_append_mm_tag() { + let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); + let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); + let bytes = hex::decode(blocktemplate_blob).unwrap(); + let mut block = deserialize::(&bytes[..]).unwrap(); + let mut block_header = BlockHeader { + version: 0, + height: 0, + prev_hash: FixedHash::zero(), + timestamp: EpochTime::now(), + output_mr: FixedHash::zero(), + output_smt_size: 0, + kernel_mr: FixedHash::zero(), + kernel_mmr_size: 0, + input_mr: FixedHash::zero(), + total_kernel_offset: Default::default(), + total_script_offset: Default::default(), + nonce: 0, + pow: ProofOfWork::default(), + validator_node_mr: FixedHash::zero(), + validator_node_size: 0, + }; + let hash = block_header.merge_mining_hash(); + insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&mut block, hash, 1, 0).unwrap(); + let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); + let mut hashes = Vec::with_capacity(count as usize); + hashes.push(block.miner_tx.hash()); + // Note: tx_hashes is empty, so |hashes| == 1 + for item in block.clone().tx_hashes { + hashes.push(item); + } + let root = tree_hash(&hashes).unwrap(); + assert_eq!(root, hashes[0]); + let coinbase_merkle_proof = create_merkle_proof(&hashes, &hashes[0]).unwrap(); + let aux_hashes = vec![monero::Hash::from_slice(hash.as_ref())]; + let aux_chain_merkle_proof = create_merkle_proof(&aux_hashes, &aux_hashes[0]).unwrap(); + let monero_data = MoneroPowData { + header: block.header, + randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), + transaction_count: count, + merkle_root: root, + coinbase_merkle_proof, + coinbase_tx: block.miner_tx, + aux_chain_merkle_proof, + }; + let mut serialized = Vec::new(); + monero_data.serialize(&mut serialized).unwrap(); + let pow = ProofOfWork { + pow_algo: PowAlgorithm::RandomX, + pow_data: serialized, + }; + block_header.pow = pow; + verify_header(&block_header, &hash).unwrap(); + } + + #[test] + fn test_append_mm_tag_no_tag() { + let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); + let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); + let bytes = hex::decode(blocktemplate_blob).unwrap(); + let block = deserialize::(&bytes[..]).unwrap(); + let mut block_header = BlockHeader { + version: 0, + height: 0, + prev_hash: FixedHash::zero(), + timestamp: EpochTime::now(), + output_mr: FixedHash::zero(), + output_smt_size: 0, + kernel_mr: FixedHash::zero(), + kernel_mmr_size: 0, + input_mr: FixedHash::zero(), + total_kernel_offset: Default::default(), + total_script_offset: Default::default(), + nonce: 0, + pow: ProofOfWork::default(), + validator_node_mr: FixedHash::zero(), + validator_node_size: 0, + }; + let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); + let mut hashes = Vec::with_capacity(count as usize); + hashes.push(block.miner_tx.hash()); + for item in block.clone().tx_hashes { + hashes.push(item); + } + let root = tree_hash(&hashes).unwrap(); + let coinbase_merkle_proof = create_merkle_proof(&hashes, &hashes[0]).unwrap(); + let aux_hashes = vec![monero::Hash::from_slice(block_header.hash().as_ref())]; + let aux_chain_merkle_proof = create_merkle_proof(&aux_hashes, &aux_hashes[0]).unwrap(); + let monero_data = MoneroPowData { + header: block.header, + randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), + transaction_count: count, + merkle_root: root, + coinbase_merkle_proof, + coinbase_tx: block.miner_tx, + aux_chain_merkle_proof, + }; + + let mut serialized = Vec::new(); + monero_data.serialize(&mut serialized).unwrap(); + let pow = ProofOfWork { + pow_algo: PowAlgorithm::RandomX, + pow_data: serialized, + }; + block_header.pow = pow; + let err = verify_header(&block_header, &block_header.hash()).unwrap_err(); + unpack_enum!(MergeMineError::ValidationError(details) = err); + assert!(details.contains("Expected merge mining tag was not found in Monero coinbase transaction")); + } + + #[test] + fn test_append_mm_tag_wrong_hash() { + let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); + let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); + let bytes = hex::decode(blocktemplate_blob).unwrap(); + let mut block = deserialize::(&bytes[..]).unwrap(); + let mut block_header = BlockHeader { + version: 0, + height: 0, + prev_hash: FixedHash::zero(), + timestamp: EpochTime::now(), + output_mr: FixedHash::zero(), + output_smt_size: 0, + kernel_mr: FixedHash::zero(), + kernel_mmr_size: 0, + input_mr: FixedHash::zero(), + total_kernel_offset: Default::default(), + total_script_offset: Default::default(), + nonce: 0, + pow: ProofOfWork::default(), + validator_node_mr: FixedHash::zero(), + validator_node_size: 0, + }; + let hash = Hash::null(); + insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&mut block, hash, 1, 0).unwrap(); + let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); + let mut hashes = Vec::with_capacity(count as usize); + let mut proof = Vec::with_capacity(count as usize); + hashes.push(block.miner_tx.hash()); + proof.push(block.miner_tx.hash()); + for item in block.clone().tx_hashes { + hashes.push(item); + proof.push(item); + } + let root = tree_hash(&hashes).unwrap(); + let coinbase_merkle_proof = create_merkle_proof(&hashes, &hashes[0]).unwrap(); + let aux_hashes = vec![monero::Hash::from_slice(hash.as_ref())]; + let aux_chain_merkle_proof = create_merkle_proof(&aux_hashes, &aux_hashes[0]).unwrap(); + let monero_data = MoneroPowData { + header: block.header, + randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), + transaction_count: count, + merkle_root: root, + coinbase_merkle_proof, + coinbase_tx: block.miner_tx, + aux_chain_merkle_proof, + }; + let mut serialized = Vec::new(); + monero_data.serialize(&mut serialized).unwrap(); + let pow = ProofOfWork { + pow_algo: PowAlgorithm::RandomX, + pow_data: serialized, + }; + block_header.pow = pow; + let err = verify_header(&block_header, &block_header.hash()).unwrap_err(); + unpack_enum!(MergeMineError::ValidationError(details) = err); + assert!(details.contains("Expected merge mining tag was not found in Monero coinbase transaction")); + } + + #[test] + fn test_duplicate_append_mm_tag() { + let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); + let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); + let bytes = hex::decode(blocktemplate_blob).unwrap(); + let mut block = deserialize::(&bytes[..]).unwrap(); + let mut block_header = BlockHeader { + version: 0, + height: 0, + prev_hash: FixedHash::zero(), + timestamp: EpochTime::now(), + output_mr: FixedHash::zero(), + output_smt_size: 0, + kernel_mr: FixedHash::zero(), + kernel_mmr_size: 0, + input_mr: FixedHash::zero(), + total_kernel_offset: Default::default(), + total_script_offset: Default::default(), + nonce: 0, + pow: ProofOfWork::default(), + validator_node_mr: FixedHash::zero(), + validator_node_size: 0, + }; + let hash = block_header.merge_mining_hash(); + insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&mut block, hash, 1, 0).unwrap(); + #[allow(clippy::redundant_clone)] + let mut block_header2 = block_header.clone(); + block_header2.version = 1; + let hash2 = block_header2.merge_mining_hash(); + assert!(extract_aux_merkle_root_from_block(&block).is_ok()); + + // Try via the API - this will fail because more than one merge mining tag is not allowed + assert!(insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&mut block, hash2, 1, 0).is_err()); + + // Now bypass the API - this will effectively allow us to insert more than one merge mining tag, + // like trying to sneek it in. Later on, when we call `verify_header(&block_header)`, it should fail. + let mut extra_field = ExtraField::try_parse(&block.miner_tx.prefix.extra).unwrap(); + let hash = monero::Hash::from_slice(hash.as_ref()); + extra_field.0.insert(0, SubField::MergeMining(Some(VarInt(0)), hash)); + block.miner_tx.prefix.extra = extra_field.into(); + + // Trying to extract the Tari hash will fail because there are more than one merge mining tag + let err = extract_aux_merkle_root_from_block(&block).unwrap_err(); + unpack_enum!(MergeMineError::ValidationError(details) = err); + assert!(details.contains("More than one merge mining tag found in coinbase")); + + let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); + let mut hashes = Vec::with_capacity(count as usize); + hashes.push(block.miner_tx.hash()); + // Note: tx_hashes is empty, so |hashes| == 1 + for item in block.clone().tx_hashes { + hashes.push(item); + } + let root = tree_hash(&hashes).unwrap(); + assert_eq!(root, hashes[0]); + let coinbase_merkle_proof = create_merkle_proof(&hashes, &hashes[0]).unwrap(); + let aux_hashes = vec![monero::Hash::from_slice(hash.as_ref())]; + let aux_chain_merkle_proof = create_merkle_proof(&aux_hashes, &aux_hashes[0]).unwrap(); + let monero_data = MoneroPowData { + header: block.header, + randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), + transaction_count: count, + merkle_root: root, + coinbase_merkle_proof, + coinbase_tx: block.miner_tx, + aux_chain_merkle_proof, + }; + let mut serialized = Vec::new(); + monero_data.serialize(&mut serialized).unwrap(); + let pow = ProofOfWork { + pow_algo: PowAlgorithm::RandomX, + pow_data: serialized, + }; + block_header.pow = pow; + + // Header verification will fail because there are more than one merge mining tag + let err = verify_header(&block_header, &block_header.hash()).unwrap_err(); + unpack_enum!(MergeMineError::ValidationError(details) = err); + assert!(details.contains("More than one merge mining tag found in coinbase")); + } + + #[test] + fn test_extra_field_with_parsing_error() { + let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); + let bytes = hex::decode(blocktemplate_blob).unwrap(); + let mut block = deserialize::(&bytes[..]).unwrap(); + let block_header = BlockHeader { + version: 0, + height: 0, + prev_hash: FixedHash::zero(), + timestamp: EpochTime::now(), + output_mr: FixedHash::zero(), + output_smt_size: 0, + kernel_mr: FixedHash::zero(), + kernel_mmr_size: 0, + input_mr: FixedHash::zero(), + total_kernel_offset: Default::default(), + total_script_offset: Default::default(), + nonce: 0, + pow: ProofOfWork::default(), + validator_node_mr: FixedHash::zero(), + validator_node_size: 2, + }; + + // Let us manipulate the extra field to make it invalid + let mut extra_field_before_parse = ExtraField::try_parse(&block.miner_tx.prefix.extra).unwrap(); + assert_eq!( + "ExtraField([TxPublicKey(06225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa4782), Nonce([246, \ + 58, 168, 109, 46, 133, 127, 7])])", + &format!("{:?}", extra_field_before_parse) + ); + assert!(ExtraField::try_parse(&extra_field_before_parse.clone().into()).is_ok()); + + extra_field_before_parse.0.insert(0, SubField::Padding(230)); + assert_eq!( + "ExtraField([Padding(230), TxPublicKey(06225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa4782), \ + Nonce([246, 58, 168, 109, 46, 133, 127, 7])])", + &format!("{:?}", extra_field_before_parse) + ); + assert!(ExtraField::try_parse(&extra_field_before_parse.clone().into()).is_err()); + + // Now insert the merge mining tag - this would also clean up the extra field and remove the invalid sub-fields + let hash = block_header.merge_mining_hash(); + insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&mut block, hash, 1, 0).unwrap(); + assert!(ExtraField::try_parse(&block.miner_tx.prefix.extra.clone()).is_ok()); + + // Verify that the merge mining tag is there + let extra_field_after_tag = ExtraField::try_parse(&block.miner_tx.prefix.extra.clone()).unwrap(); + assert_eq!( + &format!( + "ExtraField([MergeMining(Some(3458764513820540928), 0x{}), \ + TxPublicKey(06225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa4782), Nonce([246, 58, 168, \ + 109, 46, 133, 127, 7])])", + hex::encode(hash) + ), + &format!("{:?}", extra_field_after_tag) + ); + } + + #[test] + fn test_verify_header_no_coinbase() { + let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); + let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); + let bytes = hex::decode(blocktemplate_blob).unwrap(); + let mut block = deserialize::(&bytes[..]).unwrap(); + let mut block_header = BlockHeader { + version: 0, + height: 0, + prev_hash: FixedHash::zero(), + timestamp: EpochTime::now(), + output_mr: FixedHash::zero(), + output_smt_size: 0, + kernel_mr: FixedHash::zero(), + kernel_mmr_size: 0, + input_mr: FixedHash::zero(), + total_kernel_offset: Default::default(), + total_script_offset: Default::default(), + nonce: 0, + pow: ProofOfWork::default(), + validator_node_mr: FixedHash::zero(), + validator_node_size: 0, + }; + let hash = block_header.merge_mining_hash(); + insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&mut block, hash, 1, 0).unwrap(); + let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); + let mut hashes = Vec::with_capacity(count as usize); + let mut proof = Vec::with_capacity(count as usize); + hashes.push(block.miner_tx.hash()); + proof.push(block.miner_tx.hash()); + for item in block.clone().tx_hashes { + hashes.push(item); + proof.push(item); + } + let root = tree_hash(&hashes).unwrap(); + let coinbase_merkle_proof = create_merkle_proof(&hashes, &hashes[0]).unwrap(); + let aux_hashes = vec![monero::Hash::from_slice(hash.as_ref())]; + let aux_chain_merkle_proof = create_merkle_proof(&aux_hashes, &aux_hashes[0]).unwrap(); + let monero_data = MoneroPowData { + header: block.header, + randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), + transaction_count: count, + merkle_root: root, + coinbase_merkle_proof, + coinbase_tx: Default::default(), + aux_chain_merkle_proof, + }; + let mut serialized = Vec::new(); + monero_data.serialize(&mut serialized).unwrap(); + let pow = ProofOfWork { + pow_algo: PowAlgorithm::RandomX, + pow_data: serialized, + }; + block_header.pow = pow; + let err = verify_header(&block_header, &block_header.hash()).unwrap_err(); + unpack_enum!(MergeMineError::ValidationError(details) = err); + assert!(details.contains("Expected merge mining tag was not found in Monero coinbase transaction")); + } + + #[test] + fn test_verify_header_no_data() { + let mut block_header = BlockHeader { + version: 0, + height: 0, + prev_hash: FixedHash::zero(), + timestamp: EpochTime::now(), + output_mr: FixedHash::zero(), + output_smt_size: 0, + kernel_mr: FixedHash::zero(), + kernel_mmr_size: 0, + input_mr: FixedHash::zero(), + total_kernel_offset: Default::default(), + total_script_offset: Default::default(), + nonce: 0, + pow: ProofOfWork::default(), + validator_node_mr: FixedHash::zero(), + validator_node_size: 0, + }; + + let monero_data = MoneroPowData { + header: Default::default(), + randomx_key: FixedByteArray::default(), + transaction_count: 1, + merkle_root: Default::default(), + coinbase_merkle_proof: create_merkle_proof(&[Hash::null()], &Hash::null()).unwrap(), + coinbase_tx: Default::default(), + aux_chain_merkle_proof: Default::default(), + }; + let mut serialized = Vec::new(); + monero_data.serialize(&mut serialized).unwrap(); + let pow = ProofOfWork { + pow_algo: PowAlgorithm::RandomX, + pow_data: serialized, + }; + block_header.pow = pow; + let err = verify_header(&block_header, &block_header.hash()).unwrap_err(); + unpack_enum!(MergeMineError::ValidationError(details) = err); + assert!(details.contains("Expected merge mining tag was not found in Monero coinbase transaction")); + } + + #[test] + fn test_verify_invalid_root() { + let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000".to_string(); + let seed_hash = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97".to_string(); + let bytes = hex::decode(blocktemplate_blob).unwrap(); + let mut block = deserialize::(&bytes[..]).unwrap(); + let mut block_header = BlockHeader { + version: 0, + height: 0, + prev_hash: FixedHash::zero(), + timestamp: EpochTime::now(), + output_mr: FixedHash::zero(), + output_smt_size: 0, + kernel_mr: FixedHash::zero(), + kernel_mmr_size: 0, + input_mr: FixedHash::zero(), + total_kernel_offset: Default::default(), + total_script_offset: Default::default(), + nonce: 0, + pow: ProofOfWork::default(), + validator_node_mr: FixedHash::zero(), + validator_node_size: 0, + }; + let hash = block_header.merge_mining_hash(); + insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&mut block, hash, 1, 0).unwrap(); + let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); + let mut hashes = Vec::with_capacity(count as usize); + let mut proof = Vec::with_capacity(count as usize); + hashes.push(block.miner_tx.hash()); + proof.push(block.miner_tx.hash()); + for item in block.clone().tx_hashes { + hashes.push(item); + proof.push(item); + } + + let coinbase_merkle_proof = create_merkle_proof(&hashes, &hashes[0]).unwrap(); + let aux_hashes = vec![monero::Hash::from_slice(hash.as_ref())]; + let aux_chain_merkle_proof = create_merkle_proof(&aux_hashes, &aux_hashes[0]).unwrap(); + let monero_data = MoneroPowData { + header: block.header, + randomx_key: FixedByteArray::from_canonical_bytes(&from_hex(&seed_hash).unwrap()).unwrap(), + transaction_count: count, + merkle_root: Hash::null(), + coinbase_merkle_proof, + coinbase_tx: block.miner_tx, + aux_chain_merkle_proof, + }; + let mut serialized = Vec::new(); + monero_data.serialize(&mut serialized).unwrap(); + let pow = ProofOfWork { + pow_algo: PowAlgorithm::RandomX, + pow_data: serialized, + }; + block_header.pow = pow; + let err = verify_header(&block_header, &block_header.hash()).unwrap_err(); + unpack_enum!(MergeMineError::InvalidMerkleRoot = err); + } + + #[test] + fn test_difficulty() { + // Taken from block: https://stagenet.xmrchain.net/search?value=672576 + let versions = "0c0c"; + // Tool for encoding VarInts: + // https://gchq.github.io/CyberChef/#recipe=VarInt_Encode()To_Hex('Space',0)From_Hex('Auto'/disabled)VarInt_Decode(/disabled)&input=MTYwMTAzMTIwMg + let timestamp = "a298b7fb05"; // 1601031202 + let prev_block = "046f4fe371f9acdc27c377f4adee84e93b11f89246a74dd77f1bf0856141da5c"; + let nonce = "FE394F12"; // 307182078 + let tx_hash = "77139305ea53cfe95cf7235d2fed6fca477395b019b98060acdbc0f8fb0b8b92"; // miner tx + let count = "01"; + + let input = from_hex(&format!( + "{}{}{}{}{}{}", + versions, timestamp, prev_block, nonce, tx_hash, count + )) + .unwrap(); + let key = from_hex("2aca6501719a5c7ab7d4acbc7cc5d277b57ad8c27c6830788c2d5a596308e5b1").unwrap(); + let rx = RandomXFactory::default(); + + let (difficulty, hash) = get_random_x_difficulty(&input, &rx.create(&key).unwrap()).unwrap(); + assert_eq!( + hash.to_hex(), + "f68fbc8cc85bde856cd1323e9f8e6f024483038d728835de2f8c014ff6260000" + ); + assert_eq!(difficulty.as_u64(), 430603); + } +} From 5ae2955d14556056c873bade8572130d9f29c04c Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Fri, 24 Nov 2023 11:02:06 +0200 Subject: [PATCH 06/14] add 0 bypass --- .../core/src/proof_of_work/monero_rx/helpers.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs index b9e057614a..f87920a4f1 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs @@ -142,6 +142,13 @@ fn check_aux_chains( tari_hash: &FixedHash, gen_hash: &FixedHash, ) -> bool { + let t_hash = monero::Hash::from_slice(tari_hash.as_slice()); + if merge_mining_params == VarInt(0) { + // we interpret 0 as there is only 1 chain, tari. + if t_hash == *aux_chain_merkle_root { + return true; + } + } let merkle_tree_params = MerkleTreeParameters::from_varint(merge_mining_params); if merkle_tree_params.number_of_chains == 0 { return false; @@ -155,7 +162,6 @@ fn check_aux_chains( ) .low_u32() % u32::from(merkle_tree_params.number_of_chains); - let t_hash = monero::Hash::from_slice(tari_hash.as_slice()); let (merkle_root, pos) = monero_data.aux_chain_merkle_proof.calculate_root_with_pos(&t_hash); if hash_position != pos { return false; @@ -292,13 +298,12 @@ pub fn insert_merge_mining_tag_and_aux_chain_merkle_root_into_block Date: Fri, 24 Nov 2023 11:10:14 +0200 Subject: [PATCH 07/14] fixed test --- base_layer/core/src/proof_of_work/monero_rx/helpers.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs index f87920a4f1..367229f2a5 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs @@ -140,7 +140,7 @@ fn check_aux_chains( merge_mining_params: VarInt, aux_chain_merkle_root: &monero::Hash, tari_hash: &FixedHash, - gen_hash: &FixedHash, + tari_genesis_block_hash: &FixedHash, ) -> bool { let t_hash = monero::Hash::from_slice(tari_hash.as_slice()); if merge_mining_params == VarInt(0) { @@ -155,7 +155,7 @@ fn check_aux_chains( } let hash_position = U256::from_little_endian( &Sha256::new() - .chain_update(gen_hash) + .chain_update(tari_genesis_block_hash) .chain_update(merkle_tree_params.aux_nonce.to_le_bytes()) .chain_update((109_u8).to_le_bytes()) .finalize(), @@ -791,7 +791,7 @@ mod test { let extra_field_after_tag = ExtraField::try_parse(&block.miner_tx.prefix.extra.clone()).unwrap(); assert_eq!( &format!( - "ExtraField([MergeMining(Some(3458764513820540928), 0x{}), \ + "ExtraField([MergeMining(Some(0), 0x{}), \ TxPublicKey(06225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa4782), Nonce([246, 58, 168, \ 109, 46, 133, 127, 7])])", hex::encode(hash) From 425110e90bd7fda8a10f13d93fc729299fda0f40 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Fri, 24 Nov 2023 12:28:46 +0200 Subject: [PATCH 08/14] small update --- .../core/src/proof_of_work/monero_rx/error.rs | 4 +++- .../core/src/proof_of_work/monero_rx/helpers.rs | 17 ++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) 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 ab7d7958e5..6e825dde2e 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 @@ -48,6 +48,8 @@ pub enum MergeMineError { InvalidMerkleRoot, #[error("Invalid difficulty: {0}")] DifficultyError(#[from] DifficultyError), + #[error("Cannot mine with 0 aux chains")] + ZeroAuxChains, } impl MergeMineError { @@ -64,7 +66,7 @@ impl MergeMineError { reason: err.to_string(), ban_duration: BanPeriod::Long, }), - MergeMineError::RandomXVMFactoryError(_) => None, + MergeMineError::RandomXVMFactoryError(_)| MergeMineError::ZeroAuxChains => None, } } } diff --git a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs index 367229f2a5..ce785a5414 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs @@ -270,9 +270,12 @@ pub fn create_ordered_transaction_hashes_from_block(block: &monero::Block) -> Ve pub fn insert_merge_mining_tag_and_aux_chain_merkle_root_into_block>( block: &mut monero::Block, hash: T, - aux_number: u8, + aux_chain_count: u8, aux_nonce: u32, ) -> Result<(), MergeMineError> { + if aux_chain_count == 0{ + return Err(MergeMineError::ZeroAuxChains) + } if hash.as_ref().len() != monero::Hash::len_bytes() { return Err(MergeMineError::HashingError(format!( "Expected source to be {} bytes, but it was {} bytes", @@ -298,12 +301,16 @@ pub fn insert_merge_mining_tag_and_aux_chain_merkle_root_into_block Date: Mon, 27 Nov 2023 09:13:44 +0200 Subject: [PATCH 09/14] fmt --- base_layer/core/src/proof_of_work/monero_rx/error.rs | 2 +- base_layer/core/src/proof_of_work/monero_rx/helpers.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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 6e825dde2e..1517ff9f0f 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 @@ -66,7 +66,7 @@ impl MergeMineError { reason: err.to_string(), ban_duration: BanPeriod::Long, }), - MergeMineError::RandomXVMFactoryError(_)| MergeMineError::ZeroAuxChains => None, + MergeMineError::RandomXVMFactoryError(_) | MergeMineError::ZeroAuxChains => None, } } } diff --git a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs index ce785a5414..0c4418bb74 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs @@ -273,8 +273,8 @@ pub fn insert_merge_mining_tag_and_aux_chain_merkle_root_into_block Result<(), MergeMineError> { - if aux_chain_count == 0{ - return Err(MergeMineError::ZeroAuxChains) + if aux_chain_count == 0 { + return Err(MergeMineError::ZeroAuxChains); } if hash.as_ref().len() != monero::Hash::len_bytes() { return Err(MergeMineError::HashingError(format!( From 8b7a0fd37d0def6facc0cc903bebc404dc78e173 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Wed, 29 Nov 2023 15:51:39 +0200 Subject: [PATCH 10/14] hazop changes --- Cargo.lock | 30 ++++++- base_layer/core/Cargo.toml | 1 + .../src/proof_of_work/monero_rx/helpers.rs | 11 +++ .../proof_of_work/monero_rx/merkle_tree.rs | 84 +++++++++++++++++-- .../monero_rx/merkle_tree_parameters.rs | 1 + 5 files changed, 118 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3e6722b1c7..2537059b88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1797,6 +1797,16 @@ dependencies = [ "termcolor", ] +[[package]] +name = "env_logger" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +dependencies = [ + "log", + "regex", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -3294,7 +3304,7 @@ dependencies = [ "diesel", "diesel_migrations", "digest 0.10.7", - "env_logger", + "env_logger 0.7.1", "fs2", "futures 0.3.29", "itertools 0.10.5", @@ -4422,6 +4432,17 @@ dependencies = [ "memchr", ] +[[package]] +name = "quickcheck" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" +dependencies = [ + "env_logger 0.8.4", + "log", + "rand", +] + [[package]] name = "quote" version = "1.0.33" @@ -5601,7 +5622,7 @@ dependencies = [ "data-encoding", "derivative", "digest 0.10.7", - "env_logger", + "env_logger 0.7.1", "futures 0.3.29", "lmdb-zero", "log", @@ -5650,7 +5671,7 @@ dependencies = [ "diesel", "diesel_migrations", "digest 0.10.7", - "env_logger", + "env_logger 0.7.1", "futures 0.3.29", "futures-test", "futures-util", @@ -5745,7 +5766,7 @@ dependencies = [ "decimal-rs", "derivative", "digest 0.10.7", - "env_logger", + "env_logger 0.7.1", "fs2", "futures 0.3.29", "hex", @@ -5762,6 +5783,7 @@ dependencies = [ "once_cell", "primitive-types", "prost 0.9.0", + "quickcheck", "rand", "randomx-rs", "serde", diff --git a/base_layer/core/Cargo.toml b/base_layer/core/Cargo.toml index 193291462e..5a0d9c7c39 100644 --- a/base_layer/core/Cargo.toml +++ b/base_layer/core/Cargo.toml @@ -88,6 +88,7 @@ config = { version = "0.13.0" } env_logger = "0.7.0" tempfile = "3.1.0" toml = { version = "0.5" } +quickcheck = "1.0" [build-dependencies] tari_common = { path = "../../common", features = ["build"] } diff --git a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs index 0c4418bb74..cd49b53c27 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs @@ -313,6 +313,17 @@ pub fn insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&bytes[..]) + .map_err(|_| MergeMineError::ValidationError("blocktemplate blob invalid".to_string()))?; + if block != &de_block { + return Err(MergeMineError::SerializeError( + "Blocks dont match after serialization".to_string(), + )); + } Ok(()) } 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 8e268caf15..57ef8d92f8 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 @@ -35,7 +35,8 @@ use monero::{ use crate::proof_of_work::monero_rx::error::MergeMineError; -const MAX_MERKLE_TREE_BYTES: usize = 4096; +// Binary tree of depth 32 means u32::MAX tree, this is more than large enough to support most trees. +const MAX_MERKLE_TREE_PROOF_SIZE: usize = 32; /// Returns the Keccak 256 hash of the byte input fn cn_fast_hash(data: &[u8]) -> Hash { @@ -137,7 +138,7 @@ impl BorshSerialize for MerkleProof { for hash in &self.branch { hash.consensus_encode(writer)?; } - BorshSerialize::serialize(&self.path_bitmap, writer)?; + writer.write_varint(self.path_bitmap)?; Ok(()) } } @@ -146,10 +147,10 @@ impl BorshDeserialize for MerkleProof { fn deserialize_reader(reader: &mut R) -> Result where R: io::Read { let len = reader.read_varint()?; - if len > MAX_MERKLE_TREE_BYTES { + if len > MAX_MERKLE_TREE_PROOF_SIZE { return Err(io::Error::new( io::ErrorKind::InvalidInput, - "Larger than max merkle tree bytes".to_string(), + "Larger than max merkle tree length".to_string(), )); } let mut branch = Vec::with_capacity(len); @@ -159,13 +160,16 @@ impl BorshDeserialize for MerkleProof { .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err.to_string()))?, ); } - let path_bitmap = BorshDeserialize::deserialize_reader(reader)?; + let path_bitmap = reader.read_varint()?; Ok(Self { branch, path_bitmap }) } } impl MerkleProof { fn try_construct(branch: Vec, path_bitmap: u32) -> Option { + if branch.len() > MAX_MERKLE_TREE_PROOF_SIZE { + return None; + } Some(Self { branch, path_bitmap }) } @@ -212,6 +216,7 @@ impl MerkleProof { root = cn_fast_hash2(&root, &self.branch[d]); pos += multiplier; } + // this cant overflow as the max depth is 32, and 2^32 == u32::MAX multiplier *= 2; } @@ -312,7 +317,76 @@ mod test { use super::*; use crate::proof_of_work::randomx_factory::RandomXFactory; + mod quicktest { + use monero::Hash; + use quickcheck::{quickcheck, Arbitrary, Gen}; + + use crate::proof_of_work::monero_rx::merkle_tree::{MerkleProof, MAX_MERKLE_TREE_PROOF_SIZE}; + + #[derive(Clone, Debug)] + struct QuickHash { + pub bits: Vec, + } + + impl Arbitrary for QuickHash { + fn arbitrary(g: &mut Gen) -> QuickHash { + let mut hash = Vec::new(); + for _ in 0..32 { + hash.push(u8::arbitrary(g)); + } + QuickHash { bits: hash } + } + } + + fn create_monero_hashes(input_vec: Vec) -> Vec { + input_vec + .into_iter() + .map(|v| Hash::from_slice(v.bits.as_slice())) + .collect() + } + #[test] + fn test_create() { + fn try_create(input_vec: Vec, path: u32) -> bool { + let hashes = create_monero_hashes(input_vec); + let length = hashes.len(); + let res = MerkleProof::try_construct(hashes, path); + if length > MAX_MERKLE_TREE_PROOF_SIZE { + return res.is_none(); + } + res.is_some() + } + quickcheck(try_create as fn(Vec, u32) -> bool) + } + #[test] + fn test_proof() { + fn proof_first(input_vec: Vec, path: u32) -> bool { + let hashes = create_monero_hashes(input_vec); + let hash = hashes[0]; + let length = hashes.len(); + if length > MAX_MERKLE_TREE_PROOF_SIZE { + return true; + } + let proof = MerkleProof::try_construct(hashes, path).unwrap(); + proof.calculate_root(&hash); + true + } + + fn proof_random(input_vec: Vec, hash: QuickHash, path: u32) -> bool { + let hashes = create_monero_hashes(input_vec); + let hash = Hash::from_slice(hash.bits.as_slice()); + let length = hashes.len(); + if length > MAX_MERKLE_TREE_PROOF_SIZE { + return true; + } + let proof = MerkleProof::try_construct(hashes, path).unwrap(); + proof.calculate_root(&hash); + true + } + quickcheck(proof_first as fn(Vec, u32) -> bool); + quickcheck(proof_random as fn(Vec, QuickHash, u32) -> bool); + } + } mod tree_hash { use super::*; diff --git a/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs b/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs index fb79dd1024..e227db85f5 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs @@ -24,6 +24,7 @@ use std::{cmp::min, convert::TryFrom}; use monero::VarInt; +// This is based on https://github.com/SChernykh/p2pool/blob/merge-mining/docs/MERGE_MINING.MD#merge-mining-tx_extra-tag-format #[derive(Debug)] pub struct MerkleTreeParameters { pub number_of_chains: u8, From a47dd9cfa2732da565fe2fb3086b90fdf29d8a79 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Wed, 29 Nov 2023 16:40:15 +0200 Subject: [PATCH 11/14] fix mt bug --- .../src/proof_of_work/monero_rx/helpers.rs | 2 +- .../monero_rx/merkle_tree_parameters.rs | 69 ++++++++++++++----- 2 files changed, 51 insertions(+), 20 deletions(-) diff --git a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs index cd49b53c27..1e0212f722 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs @@ -314,7 +314,7 @@ pub fn insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&bytes[..]) diff --git a/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs b/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs index e227db85f5..b7a945c439 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs @@ -25,7 +25,7 @@ use std::{cmp::min, convert::TryFrom}; use monero::VarInt; // This is based on https://github.com/SChernykh/p2pool/blob/merge-mining/docs/MERGE_MINING.MD#merge-mining-tx_extra-tag-format -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq)] pub struct MerkleTreeParameters { pub number_of_chains: u8, pub aux_nonce: u32, @@ -33,7 +33,7 @@ pub struct MerkleTreeParameters { impl MerkleTreeParameters { pub fn from_varint(merkle_tree_varint: VarInt) -> MerkleTreeParameters { - let bits = get_decode_bits(merkle_tree_varint.0); + let bits = get_decode_bits(merkle_tree_varint.0) + 1; let number_of_chains = get_aux_chain_count(merkle_tree_varint.0, bits); let aux_nonce = get_aux_nonce(merkle_tree_varint.0, bits); MerkleTreeParameters { @@ -45,16 +45,15 @@ impl MerkleTreeParameters { pub fn to_varint(&self) -> VarInt { let size = u8::try_from(self.number_of_chains.leading_zeros()) .expect("This cant fail, u8 can only have 8 leading 0's which will fit in 255"); - let mut bits = encode_bits(8 - size); - let mut n = encode_aux_chain_count(self.number_of_chains, 8 - size); - let mut nonce = encode_aux_nonce(self.aux_nonce); - bits.append(&mut n); - bits.append(&mut nonce); - if bits.len() < 64 { - let mut missing_zeroes = vec![0; 64 - bits.len()]; - bits.append(&mut missing_zeroes); - } - let num: u64 = bits.iter().fold(0, |result, &bit| (result << 1) ^ u64::from(bit)); + let mut size_bits = encode_bits(7 - size); + let mut n_bits = encode_aux_chain_count(self.number_of_chains, 8 - size); + let mut nonce_bits = encode_aux_nonce(self.aux_nonce); + // this wont underflow as max size will be size_bits(3) + n_bits(8) + nonce_bits(32) = 43 + let mut zero_bits = vec![0; 64 - size_bits.len() - n_bits.len() - nonce_bits.len()]; + size_bits.append(&mut n_bits); + size_bits.append(&mut nonce_bits); + size_bits.append(&mut zero_bits); + let num: u64 = size_bits.iter().fold(0, |result, &bit| (result << 1) ^ u64::from(bit)); VarInt(num) } } @@ -91,13 +90,16 @@ fn encode_aux_nonce(num: u32) -> Vec { #[cfg(test)] mod test { - use crate::proof_of_work::monero_rx::merkle_tree_parameters::{ - encode_aux_chain_count, - encode_aux_nonce, - encode_bits, - get_aux_chain_count, - get_aux_nonce, - get_decode_bits, + use crate::proof_of_work::monero_rx::{ + merkle_tree_parameters::{ + encode_aux_chain_count, + encode_aux_nonce, + encode_bits, + get_aux_chain_count, + get_aux_nonce, + get_decode_bits, + }, + MerkleTreeParameters, }; #[test] @@ -247,4 +249,33 @@ mod test { ]; assert_eq!(bits, array); } + + mod quicktest { + use quickcheck::{quickcheck, Arbitrary, Gen}; + + use crate::proof_of_work::monero_rx::MerkleTreeParameters; + + impl Arbitrary for MerkleTreeParameters { + fn arbitrary(g: &mut Gen) -> MerkleTreeParameters { + let mut mt = MerkleTreeParameters { + number_of_chains: u8::arbitrary(g), + aux_nonce: u32::arbitrary(g), + }; + if mt.number_of_chains == 0 { + mt.number_of_chains = 1; + }; + mt + } + } + + #[test] + fn test_ser_deserialize() { + fn varint_serialization(mt_params: MerkleTreeParameters) -> bool { + let varint = mt_params.to_varint(); + let deserialize = MerkleTreeParameters::from_varint(varint); + mt_params == deserialize + } + quickcheck(varint_serialization as fn(MerkleTreeParameters) -> bool) + } + } } From f8388b944e53566b8510e0351a2d65db1d1966f4 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Wed, 29 Nov 2023 16:58:33 +0200 Subject: [PATCH 12/14] clippy --- .../monero_rx/merkle_tree_parameters.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs b/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs index b7a945c439..fc785315d1 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/merkle_tree_parameters.rs @@ -90,16 +90,13 @@ fn encode_aux_nonce(num: u32) -> Vec { #[cfg(test)] mod test { - use crate::proof_of_work::monero_rx::{ - merkle_tree_parameters::{ - encode_aux_chain_count, - encode_aux_nonce, - encode_bits, - get_aux_chain_count, - get_aux_nonce, - get_decode_bits, - }, - MerkleTreeParameters, + use crate::proof_of_work::monero_rx::merkle_tree_parameters::{ + encode_aux_chain_count, + encode_aux_nonce, + encode_bits, + get_aux_chain_count, + get_aux_nonce, + get_decode_bits, }; #[test] From 629b2e70feda7e8b905de1c3f4291ced3f130691 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Wed, 29 Nov 2023 17:21:49 +0200 Subject: [PATCH 13/14] fix quickcheck edge case --- base_layer/core/src/proof_of_work/monero_rx/merkle_tree.rs | 3 +++ 1 file changed, 3 insertions(+) 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 57ef8d92f8..e56790cda7 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 @@ -361,6 +361,9 @@ mod test { #[test] fn test_proof() { fn proof_first(input_vec: Vec, path: u32) -> bool { + if input_vec.is_empty() { + return true; + } let hashes = create_monero_hashes(input_vec); let hash = hashes[0]; let length = hashes.len(); From 851d165b7cb118b1a11b93afe725f9f2f2875c5f Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Wed, 29 Nov 2023 20:49:58 +0200 Subject: [PATCH 14/14] fix overflow --- .../core/src/proof_of_work/monero_rx/merkle_tree.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 e56790cda7..9eaeede7b7 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 @@ -147,7 +147,7 @@ impl BorshDeserialize for MerkleProof { fn deserialize_reader(reader: &mut R) -> Result where R: io::Read { let len = reader.read_varint()?; - if len > MAX_MERKLE_TREE_PROOF_SIZE { + if len >= MAX_MERKLE_TREE_PROOF_SIZE { return Err(io::Error::new( io::ErrorKind::InvalidInput, "Larger than max merkle tree length".to_string(), @@ -167,7 +167,7 @@ impl BorshDeserialize for MerkleProof { impl MerkleProof { fn try_construct(branch: Vec, path_bitmap: u32) -> Option { - if branch.len() > MAX_MERKLE_TREE_PROOF_SIZE { + if branch.len() >= MAX_MERKLE_TREE_PROOF_SIZE { return None; } Some(Self { branch, path_bitmap }) @@ -350,7 +350,7 @@ mod test { let hashes = create_monero_hashes(input_vec); let length = hashes.len(); let res = MerkleProof::try_construct(hashes, path); - if length > MAX_MERKLE_TREE_PROOF_SIZE { + if length >= MAX_MERKLE_TREE_PROOF_SIZE { return res.is_none(); } res.is_some() @@ -367,7 +367,7 @@ mod test { let hashes = create_monero_hashes(input_vec); let hash = hashes[0]; let length = hashes.len(); - if length > MAX_MERKLE_TREE_PROOF_SIZE { + if length >= MAX_MERKLE_TREE_PROOF_SIZE { return true; } let proof = MerkleProof::try_construct(hashes, path).unwrap(); @@ -379,7 +379,7 @@ mod test { let hashes = create_monero_hashes(input_vec); let hash = Hash::from_slice(hash.bits.as_slice()); let length = hashes.len(); - if length > MAX_MERKLE_TREE_PROOF_SIZE { + if length >= MAX_MERKLE_TREE_PROOF_SIZE { return true; } let proof = MerkleProof::try_construct(hashes, path).unwrap();