diff --git a/base_layer/core/src/blocks/block_header.rs b/base_layer/core/src/blocks/block_header.rs index 689ce179fd..95e19f76e1 100644 --- a/base_layer/core/src/blocks/block_header.rs +++ b/base_layer/core/src/blocks/block_header.rs @@ -74,6 +74,8 @@ pub enum BlockHeaderValidationError { ProofOfWorkError(#[from] PowError), #[error("Monero seed hash too old")] OldSeedHash, + #[error("Monero blocks must have a nonce of 0")] + InvalidNonce, #[error("Incorrect height: Expected {expected} but got {actual}")] InvalidHeight { expected: u64, actual: u64 }, #[error("Incorrect previous hash: Expected {expected} but got {actual}")] 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 55b77fa3a4..a9d778fbe6 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 @@ -72,7 +72,7 @@ fn get_random_x_difficulty(input: &[u8], vm: &RandomXVMInstance) -> Result<(Diff /// 1. The merkle proof and coinbase hash produce a matching merkle root /// /// If these assertions pass, a valid `MoneroPowData` instance is returned -fn verify_header(header: &BlockHeader) -> Result { +pub fn verify_header(header: &BlockHeader) -> Result { let monero_data = MoneroPowData::from_header(header)?; let expected_merge_mining_hash = header.mining_hash(); let extra_field = ExtraField::try_parse(&monero_data.coinbase_tx.prefix.extra) 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 530d09908f..0f17ca0b83 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 @@ -32,6 +32,7 @@ pub use helpers::{ extract_tari_hash, randomx_difficulty, serialize_monero_block_to_hex, + verify_header, }; mod fixed_array; 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 c47ce6df92..02fe20753c 100644 --- a/base_layer/core/src/validation/header/header_full_validator.rs +++ b/base_layer/core/src/validation/header/header_full_validator.rs @@ -178,6 +178,11 @@ fn check_pow_data( use PowAlgorithm::{RandomX, Sha3x}; match block_header.pow.pow_algo { RandomX => { + if block_header.nonce != 0 { + return Err(ValidationError::BlockHeaderError( + BlockHeaderValidationError::InvalidNonce, + )); + } let monero_data = MoneroPowData::from_header(block_header).map_err(|e| ValidationError::CustomError(e.to_string()))?; let seed_height = db.fetch_monero_seed_first_seen_height(&monero_data.randomx_key)?; diff --git a/base_layer/core/tests/tests/block_validation.rs b/base_layer/core/tests/tests/block_validation.rs index f68588e91c..41d43bebcb 100644 --- a/base_layer/core/tests/tests/block_validation.rs +++ b/base_layer/core/tests/tests/block_validation.rs @@ -33,7 +33,7 @@ use tari_core::{ consensus::{consensus_constants::PowAlgorithmConstants, ConsensusConstantsBuilder, ConsensusManager}, proof_of_work::{ monero_rx, - monero_rx::{FixedByteArray, MoneroPowData}, + monero_rx::{verify_header, FixedByteArray, MoneroPowData}, randomx_factory::RandomXFactory, Difficulty, PowAlgorithm, @@ -152,7 +152,26 @@ async fn test_monero_blocks() { // now lets fix the seed, and try again add_monero_data(&mut block_3, seed2); - assert_block_add_result_added(&db.add_block(Arc::new(block_3)).unwrap()); + // lets break the nonce count + let hash1 = block_3.hash(); + block_3.header.nonce = 1; + let hash2 = block_3.hash(); + assert_ne!(hash1, hash2); + assert!(verify_header(&block_3.header).is_ok()); + match db.add_block(Arc::new(block_3.clone())) { + Err(ChainStorageError::ValidationError { + source: ValidationError::BlockHeaderError(BlockHeaderValidationError::InvalidNonce), + }) => (), + Err(e) => { + panic!("Failed due to other error:{:?}", e); + }, + Ok(res) => { + panic!("Block add unexpectedly succeeded with result: {:?}", res); + }, + }; + // lets fix block3 + block_3.header.nonce = 0; + assert_block_add_result_added(&db.add_block(Arc::new(block_3.clone())).unwrap()); } fn add_monero_data(tblock: &mut Block, seed_key: &str) {