From 42cc7674177cf3bec883bbe61c5709df2c3724a7 Mon Sep 17 00:00:00 2001 From: CjS77 Date: Thu, 27 Oct 2022 11:42:30 +0100 Subject: [PATCH 1/3] feat: impl final tari pow algorithm Tari's independent proof-of-work algorithm is very straightforward. Calculate the _triple hash_ of the following input data: - Nonce (8 bytes) - Tari mining hash (32 bytes) - PoW record (for Sha-3x, this is always a single byte of value 1) That is, the nonce in little-endian format, mining hash and the PoW record are chained together and hashed by the Keccak Sha3-256 algorithm. The result is hashed again, and this result is hashed a third time. The result of the third hash is compared to the target value of the current block difficulty. A triple hash is selected to keep the requirements on hardware miners (FPGAs, ASICs) fairly low. But we also want to avoid making the proof-of-work immediately "NiceHashable". There are several coins that already use a single or double SHA3 hash, and we'd like to avoid having that hashrate immediately deployable against Tari. This PR also stabilises RFC-0131 --- applications/tari_miner/src/difficulty.rs | 6 ++--- base_layer/core/src/proof_of_work/mod.rs | 2 +- base_layer/core/src/proof_of_work/sha3_pow.rs | 22 ++++++++++--------- base_layer/core/src/test_helpers/mod.rs | 4 ++-- base_layer/core/src/validation/helpers.rs | 4 ++-- base_layer/core/src/validation/mocks.rs | 4 ++-- .../core/tests/helpers/block_builders.rs | 4 ++-- base_layer/tari_mining_helper_ffi/src/lib.rs | 10 ++++----- 8 files changed, 29 insertions(+), 27 deletions(-) diff --git a/applications/tari_miner/src/difficulty.rs b/applications/tari_miner/src/difficulty.rs index 8e283d8348..ca6a170554 100644 --- a/applications/tari_miner/src/difficulty.rs +++ b/applications/tari_miner/src/difficulty.rs @@ -23,7 +23,7 @@ use std::convert::TryInto; use tari_app_grpc::tari_rpc::BlockHeader as grpc_header; -use tari_core::{blocks::BlockHeader, proof_of_work::sha3_difficulty}; +use tari_core::{blocks::BlockHeader, proof_of_work::sha3x_difficulty}; use tari_utilities::epoch_time::EpochTime; use crate::errors::MinerError; @@ -67,7 +67,7 @@ impl BlockHeaderSha3 { #[inline] pub fn difficulty(&mut self) -> Difficulty { self.hashes = self.hashes.saturating_add(1); - sha3_difficulty(&self.header).into() + sha3x_difficulty(&self.header).into() } #[allow(clippy::cast_possible_wrap)] @@ -84,7 +84,7 @@ impl BlockHeaderSha3 { #[cfg(test)] pub mod test { use chrono::{DateTime, NaiveDate, Utc}; - use tari_core::proof_of_work::sha3_difficulty as core_sha3_difficulty; + use tari_core::proof_of_work::sha3x_difficulty as core_sha3_difficulty; use super::*; diff --git a/base_layer/core/src/proof_of_work/mod.rs b/base_layer/core/src/proof_of_work/mod.rs index 7caaa8bc1a..a368f53c2f 100644 --- a/base_layer/core/src/proof_of_work/mod.rs +++ b/base_layer/core/src/proof_of_work/mod.rs @@ -49,7 +49,7 @@ pub use proof_of_work_algorithm::PowAlgorithm; #[cfg(feature = "base_node")] mod sha3_pow; #[cfg(feature = "base_node")] -pub use sha3_pow::sha3_difficulty; +pub use sha3_pow::sha3x_difficulty; #[cfg(all(test, feature = "base_node"))] pub use sha3_pow::test as sha3_test; diff --git a/base_layer/core/src/proof_of_work/sha3_pow.rs b/base_layer/core/src/proof_of_work/sha3_pow.rs index fe56685dd5..84237c7c1e 100644 --- a/base_layer/core/src/proof_of_work/sha3_pow.rs +++ b/base_layer/core/src/proof_of_work/sha3_pow.rs @@ -27,13 +27,14 @@ use crate::{ proof_of_work::{difficulty::util::big_endian_difficulty, Difficulty}, }; -/// A simple sha3 proof of work. This is currently intended to be used for testing and perhaps Testnet until -/// Monero merge-mining is active. +/// The Tari Sha3X proof-of-work algorithm. This is the reference implementation of Tari's standalone mining +/// algorithm as described in [RFC-0131](https://rfc.tari.com/RFC-0131_Mining.html). /// -/// The proof of work difficulty is given by `H256(header )` where Hnnn is the sha3 digest of length -/// `nnn` bits. -pub fn sha3_difficulty(header: &BlockHeader) -> Difficulty { - sha3_difficulty_with_hash(header).0 +/// In short Sha3X is a triple Keccak Sha3-256 hash of the nonce, mining hash and PoW mode byte. +/// Mining using this CPU version of the algorithm is unlikely to be profitable, but is included for reference and +/// can be used to mine tXTR on testnets. +pub fn sha3x_difficulty(header: &BlockHeader) -> Difficulty { + sha3x_difficulty_with_hash(header).0 } pub fn sha3_hash(header: &BlockHeader) -> Vec { @@ -52,9 +53,10 @@ pub fn sha3_hash(header: &BlockHeader) -> Vec { .to_vec() } -fn sha3_difficulty_with_hash(header: &BlockHeader) -> (Difficulty, Vec) { +fn sha3x_difficulty_with_hash(header: &BlockHeader) -> (Difficulty, Vec) { let hash = sha3_hash(header); let hash = Sha3_256::digest(&hash); + let hash = Sha3_256::digest(&hash); let difficulty = big_endian_difficulty(&hash); (difficulty, hash.to_vec()) } @@ -66,7 +68,7 @@ pub mod test { use crate::{ blocks::BlockHeader, - proof_of_work::{sha3_pow::sha3_difficulty, Difficulty, PowAlgorithm}, + proof_of_work::{sha3_pow::sha3x_difficulty, Difficulty, PowAlgorithm}, }; /// A simple example miner. It starts at nonce = 0 and iterates until it finds a header hash that meets the desired @@ -75,7 +77,7 @@ pub mod test { fn mine_sha3(target_difficulty: Difficulty, header: &mut BlockHeader) -> u64 { header.nonce = 0; // We're mining over here! - while sha3_difficulty(header) < target_difficulty { + while sha3x_difficulty(header) < target_difficulty { header.nonce += 1; } header.nonce @@ -96,6 +98,6 @@ pub mod test { fn validate_max_target() { let mut header = get_header(); header.nonce = 1; - assert_eq!(sha3_difficulty(&header), Difficulty::from(28)); + assert_eq!(sha3x_difficulty(&header), Difficulty::from(3)); } } diff --git a/base_layer/core/src/test_helpers/mod.rs b/base_layer/core/src/test_helpers/mod.rs index aac67e4318..a9287b7951 100644 --- a/base_layer/core/src/test_helpers/mod.rs +++ b/base_layer/core/src/test_helpers/mod.rs @@ -34,7 +34,7 @@ use tari_storage::{lmdb_store::LMDBBuilder, LMDBWrapper}; use crate::{ blocks::{Block, BlockHeader, BlockHeaderAccumulatedData, ChainHeader}, consensus::{ConsensusConstants, ConsensusManager}, - proof_of_work::{sha3_difficulty, AchievedTargetDifficulty, Difficulty}, + proof_of_work::{sha3x_difficulty, AchievedTargetDifficulty, Difficulty}, transactions::{ transaction_components::{Transaction, UnblindedOutput}, CoinbaseBuilder, @@ -109,7 +109,7 @@ pub fn mine_to_difficulty(mut block: Block, difficulty: Difficulty) -> Result Result { let achieved = match block_header.pow_algo() { PowAlgorithm::Monero => monero_difficulty(block_header, randomx_factory)?, - PowAlgorithm::Sha3 => sha3_difficulty(block_header), + PowAlgorithm::Sha3 => sha3x_difficulty(block_header), }; match AchievedTargetDifficulty::try_construct(block_header.pow_algo(), target, achieved) { diff --git a/base_layer/core/src/validation/mocks.rs b/base_layer/core/src/validation/mocks.rs index f52ea98e4d..5d6e01ec90 100644 --- a/base_layer/core/src/validation/mocks.rs +++ b/base_layer/core/src/validation/mocks.rs @@ -31,7 +31,7 @@ use tari_common_types::{chain_metadata::ChainMetadata, types::Commitment}; use crate::{ blocks::{Block, BlockHeader, ChainBlock}, chain_storage::BlockchainBackend, - proof_of_work::{sha3_difficulty, AchievedTargetDifficulty, Difficulty, PowAlgorithm}, + proof_of_work::{sha3x_difficulty, AchievedTargetDifficulty, Difficulty, PowAlgorithm}, transactions::transaction_components::Transaction, validation::{ error::ValidationError, @@ -116,7 +116,7 @@ impl HeaderValidation for MockValidator { _: &DifficultyCalculator, ) -> Result { if self.is_valid.load(Ordering::SeqCst) { - let achieved = sha3_difficulty(header); + let achieved = sha3x_difficulty(header); let achieved_target = AchievedTargetDifficulty::try_construct(PowAlgorithm::Sha3, achieved - Difficulty::from(1), achieved) diff --git a/base_layer/core/tests/helpers/block_builders.rs b/base_layer/core/tests/helpers/block_builders.rs index 3dcffbb439..1564a42877 100644 --- a/base_layer/core/tests/helpers/block_builders.rs +++ b/base_layer/core/tests/helpers/block_builders.rs @@ -31,7 +31,7 @@ use tari_core::{ chain_storage::{BlockAddResult, BlockchainBackend, BlockchainDatabase, ChainStorageError}, consensus::{emission::Emission, ConsensusConstants, ConsensusManager, ConsensusManagerBuilder}, covenants::Covenant, - proof_of_work::{sha3_difficulty, AchievedTargetDifficulty, Difficulty}, + proof_of_work::{sha3x_difficulty, AchievedTargetDifficulty, Difficulty}, transactions::{ tari_amount::MicroTari, test_helpers::{ @@ -540,7 +540,7 @@ pub fn generate_new_block_with_coinbase( pub fn find_header_with_achieved_difficulty(header: &mut BlockHeader, achieved_difficulty: Difficulty) { let mut num_tries = 0; - while sha3_difficulty(header) != achieved_difficulty { + while sha3x_difficulty(header) != achieved_difficulty { header.nonce += 1; num_tries += 1; if num_tries > 10_000_000 { diff --git a/base_layer/tari_mining_helper_ffi/src/lib.rs b/base_layer/tari_mining_helper_ffi/src/lib.rs index baf22ccbb2..03ce006d4f 100644 --- a/base_layer/tari_mining_helper_ffi/src/lib.rs +++ b/base_layer/tari_mining_helper_ffi/src/lib.rs @@ -36,7 +36,7 @@ use libc::{c_char, c_int, c_uchar, c_uint, c_ulonglong}; use tari_core::{ blocks::BlockHeader, consensus::{ConsensusDecoding, ToConsensusBytes}, - proof_of_work::sha3_difficulty, + proof_of_work::sha3x_difficulty, }; use tari_crypto::tari_utilities::hex::Hex; @@ -263,7 +263,7 @@ pub unsafe extern "C" fn share_difficulty(header: *mut ByteVector, error_out: *m return 2; }, }; - let difficulty = sha3_difficulty(&block_header); + let difficulty = sha3x_difficulty(&block_header); difficulty.as_u64() } @@ -322,7 +322,7 @@ pub unsafe extern "C" fn share_validate( ptr::swap(error_out, &mut error as *mut c_int); return 2; } - let difficulty = sha3_difficulty(&block_header).as_u64(); + let difficulty = sha3x_difficulty(&block_header).as_u64(); if difficulty >= template_difficulty { 0 } else if difficulty >= share_difficulty { @@ -357,8 +357,8 @@ mod tests { let mut block = create_test_block(); block.header.nonce = rand::thread_rng().gen(); for _ in 0..20000 { - if sha3_difficulty(&block.header) >= difficulty { - return Ok((sha3_difficulty(&block.header), block.header.nonce)); + if sha3x_difficulty(&block.header) >= difficulty { + return Ok((sha3x_difficulty(&block.header), block.header.nonce)); } block.header.nonce += 1; } From d109371a6bf7e66649f39a7e93029dadab041bfc Mon Sep 17 00:00:00 2001 From: CjS77 Date: Fri, 28 Oct 2022 09:52:32 +0100 Subject: [PATCH 2/3] fix: mining_test_difficulty --- base_layer/tari_mining_helper_ffi/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base_layer/tari_mining_helper_ffi/src/lib.rs b/base_layer/tari_mining_helper_ffi/src/lib.rs index 03ce006d4f..733654f8a8 100644 --- a/base_layer/tari_mining_helper_ffi/src/lib.rs +++ b/base_layer/tari_mining_helper_ffi/src/lib.rs @@ -370,8 +370,8 @@ mod tests { #[test] fn detect_change_in_consensus_encoding() { - const NONCE: u64 = 15151693527177504675; - const DIFFICULTY: Difficulty = Difficulty::from_u64(8707); + const NONCE: u64 = 7520709748303934033; + const DIFFICULTY: Difficulty = Difficulty::from_u64(2187); unsafe { let mut error = -1; let error_ptr = &mut error as *mut c_int; From 889118b0cacfb904c8c74a28471b3f25196969da Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Thu, 3 Nov 2022 09:09:33 +0200 Subject: [PATCH 3/3] feat: update to version for PoW (#4875) Description --- Updates the header version to change the PoW so we don't have to reset the chain Motivation and Context --- See: #4862 How Has This Been Tested? --- --- .../core/src/consensus/consensus_constants.rs | 109 +++++++----------- base_layer/core/src/proof_of_work/sha3_pow.rs | 18 ++- base_layer/tari_mining_helper_ffi/src/lib.rs | 4 +- 3 files changed, 59 insertions(+), 72 deletions(-) diff --git a/base_layer/core/src/consensus/consensus_constants.rs b/base_layer/core/src/consensus/consensus_constants.rs index 09a7ab1ef3..08f0eecd03 100644 --- a/base_layer/core/src/consensus/consensus_constants.rs +++ b/base_layer/core/src/consensus/consensus_constants.rs @@ -512,73 +512,50 @@ impl ConsensusConstants { features: OutputFeaturesVersion::V0..=OutputFeaturesVersion::V0, opcode: OpcodeVersion::V0..=OpcodeVersion::V1, }; + let consensus_constants_1 = ConsensusConstants { + effective_from_height: 0, + coinbase_lock_height: 6, + blockchain_version: 0, + valid_blockchain_version_range: 0..=0, + future_time_limit: 540, + difficulty_block_window: 90, + max_block_transaction_weight: 127_795, + median_timestamp_count: 11, + emission_initial: 18_462_816_327 * uT, + emission_decay: &ESMERALDA_DECAY_PARAMS, + emission_tail: 800 * T, + max_randomx_seed_height: 3000, + proof_of_work: algos, + faucet_value: (10 * 4000) * T, + transaction_weight: TransactionWeight::v1(), + max_script_byte_size: 2048, + input_version_range, + output_version_range, + kernel_version_range, + permitted_output_types: Self::current_permitted_output_types(), + }; + let consensus_constants_2 = ConsensusConstants { + effective_from_height: 23000, + blockchain_version: 1, + valid_blockchain_version_range: 0..=1, + ..consensus_constants_1.clone() + }; + let consensus_constants_3 = ConsensusConstants { + effective_from_height: 25000, + output_version_range: output_version_2_range, + ..consensus_constants_2.clone() + }; + let consensus_constants_4 = ConsensusConstants { + effective_from_height: 33000, + blockchain_version: 2, + ..consensus_constants_3.clone() + }; + vec![ - ConsensusConstants { - effective_from_height: 0, - coinbase_lock_height: 6, - blockchain_version: 0, - valid_blockchain_version_range: 0..=0, - future_time_limit: 540, - difficulty_block_window: 90, - max_block_transaction_weight: 127_795, - median_timestamp_count: 11, - emission_initial: 18_462_816_327 * uT, - emission_decay: &ESMERALDA_DECAY_PARAMS, - emission_tail: 800 * T, - max_randomx_seed_height: 3000, - proof_of_work: algos.clone(), - faucet_value: (10 * 4000) * T, - transaction_weight: TransactionWeight::v1(), - max_script_byte_size: 2048, - input_version_range: input_version_range.clone(), - output_version_range: output_version_range.clone(), - kernel_version_range: kernel_version_range.clone(), - permitted_output_types: Self::current_permitted_output_types(), - }, - ConsensusConstants { - effective_from_height: 23000, - coinbase_lock_height: 6, - blockchain_version: 1, - valid_blockchain_version_range: 0..=1, - future_time_limit: 540, - difficulty_block_window: 90, - max_block_transaction_weight: 127_795, - median_timestamp_count: 11, - emission_initial: 18_462_816_327 * uT, - emission_decay: &ESMERALDA_DECAY_PARAMS, - emission_tail: 800 * T, - max_randomx_seed_height: 3000, - proof_of_work: algos.clone(), - faucet_value: (10 * 4000) * T, - transaction_weight: TransactionWeight::v1(), - max_script_byte_size: 2048, - input_version_range: input_version_range.clone(), - output_version_range, - kernel_version_range: kernel_version_range.clone(), - permitted_output_types: Self::current_permitted_output_types(), - }, - ConsensusConstants { - effective_from_height: 25000, - coinbase_lock_height: 6, - blockchain_version: 1, - valid_blockchain_version_range: 0..=1, - future_time_limit: 540, - difficulty_block_window: 90, - max_block_transaction_weight: 127_795, - median_timestamp_count: 11, - emission_initial: 18_462_816_327 * uT, - emission_decay: &ESMERALDA_DECAY_PARAMS, - emission_tail: 800 * T, - max_randomx_seed_height: 3000, - proof_of_work: algos, - faucet_value: (10 * 4000) * T, - transaction_weight: TransactionWeight::v1(), - max_script_byte_size: 2048, - input_version_range, - output_version_range: output_version_2_range, - kernel_version_range, - permitted_output_types: Self::current_permitted_output_types(), - }, + consensus_constants_1, + consensus_constants_2, + consensus_constants_3, + consensus_constants_4, ] } diff --git a/base_layer/core/src/proof_of_work/sha3_pow.rs b/base_layer/core/src/proof_of_work/sha3_pow.rs index 84237c7c1e..6d79061c38 100644 --- a/base_layer/core/src/proof_of_work/sha3_pow.rs +++ b/base_layer/core/src/proof_of_work/sha3_pow.rs @@ -34,7 +34,10 @@ use crate::{ /// Mining using this CPU version of the algorithm is unlikely to be profitable, but is included for reference and /// can be used to mine tXTR on testnets. pub fn sha3x_difficulty(header: &BlockHeader) -> Difficulty { - sha3x_difficulty_with_hash(header).0 + match header.version { + 2 => sha3x_difficulty_with_hash(header).0, + _ => old_sha3_difficulty_with_hash(header).0, + } } pub fn sha3_hash(header: &BlockHeader) -> Vec { @@ -61,6 +64,13 @@ fn sha3x_difficulty_with_hash(header: &BlockHeader) -> (Difficulty, Vec) { (difficulty, hash.to_vec()) } +fn old_sha3_difficulty_with_hash(header: &BlockHeader) -> (Difficulty, Vec) { + let hash = sha3_hash(header); + let hash = Sha3_256::digest(&hash); + let difficulty = big_endian_difficulty(&hash); + (difficulty, hash.to_vec()) +} + #[cfg(test)] pub mod test { use chrono::{DateTime, NaiveDate, Utc}; @@ -84,7 +94,7 @@ pub mod test { } pub fn get_header() -> BlockHeader { - let mut header = BlockHeader::new(0); + let mut header = BlockHeader::new(2); #[allow(clippy::cast_sign_loss)] let epoch_secs = @@ -97,7 +107,7 @@ pub mod test { #[test] fn validate_max_target() { let mut header = get_header(); - header.nonce = 1; - assert_eq!(sha3x_difficulty(&header), Difficulty::from(3)); + header.nonce = 14; + assert_eq!(sha3x_difficulty(&header), Difficulty::from(25)); } } diff --git a/base_layer/tari_mining_helper_ffi/src/lib.rs b/base_layer/tari_mining_helper_ffi/src/lib.rs index 733654f8a8..67c2450918 100644 --- a/base_layer/tari_mining_helper_ffi/src/lib.rs +++ b/base_layer/tari_mining_helper_ffi/src/lib.rs @@ -370,8 +370,8 @@ mod tests { #[test] fn detect_change_in_consensus_encoding() { - const NONCE: u64 = 7520709748303934033; - const DIFFICULTY: Difficulty = Difficulty::from_u64(2187); + const NONCE: u64 = 1368783905506569398; + const DIFFICULTY: Difficulty = Difficulty::from_u64(3549); unsafe { let mut error = -1; let error_ptr = &mut error as *mut c_int;