Skip to content

Commit

Permalink
feat!: impl final tari pow algorithm (#4862)
Browse files Browse the repository at this point in the history
* 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

* fix: mining_test_difficulty

* 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?
---

Co-authored-by: SW van Heerden <swvheerden@gmail.com>
  • Loading branch information
CjS77 and SWvheerden committed Nov 3, 2022
1 parent cd88484 commit a580103
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 97 deletions.
6 changes: 3 additions & 3 deletions applications/tari_miner/src/difficulty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)]
Expand All @@ -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::*;

Expand Down
109 changes: 43 additions & 66 deletions base_layer/core/src/consensus/consensus_constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
]
}

Expand Down
2 changes: 1 addition & 1 deletion base_layer/core/src/proof_of_work/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
36 changes: 24 additions & 12 deletions base_layer/core/src/proof_of_work/sha3_pow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,17 @@ 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 {
match header.version {
2 => sha3x_difficulty_with_hash(header).0,
_ => old_sha3_difficulty_with_hash(header).0,
}
}

pub fn sha3_hash(header: &BlockHeader) -> Vec<u8> {
Expand All @@ -52,7 +56,15 @@ pub fn sha3_hash(header: &BlockHeader) -> Vec<u8> {
.to_vec()
}

fn sha3_difficulty_with_hash(header: &BlockHeader) -> (Difficulty, Vec<u8>) {
fn sha3x_difficulty_with_hash(header: &BlockHeader) -> (Difficulty, Vec<u8>) {
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())
}

fn old_sha3_difficulty_with_hash(header: &BlockHeader) -> (Difficulty, Vec<u8>) {
let hash = sha3_hash(header);
let hash = Sha3_256::digest(&hash);
let difficulty = big_endian_difficulty(&hash);
Expand All @@ -66,7 +78,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
Expand All @@ -75,14 +87,14 @@ 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
}

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 =
Expand All @@ -95,7 +107,7 @@ pub mod test {
#[test]
fn validate_max_target() {
let mut header = get_header();
header.nonce = 1;
assert_eq!(sha3_difficulty(&header), Difficulty::from(28));
header.nonce = 14;
assert_eq!(sha3x_difficulty(&header), Difficulty::from(25));
}
}
4 changes: 2 additions & 2 deletions base_layer/core/src/test_helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -109,7 +109,7 @@ pub fn mine_to_difficulty(mut block: Block, difficulty: Difficulty) -> Result<Bl
// hash changing. This introduces the required entropy
block.header.nonce = rand::thread_rng().gen();
for _i in 0..20000 {
if sha3_difficulty(&block.header) == difficulty {
if sha3x_difficulty(&block.header) == difficulty {
return Ok(block);
}
block.header.nonce += 1;
Expand Down
4 changes: 2 additions & 2 deletions base_layer/core/src/validation/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ use crate::{
monero_difficulty,
monero_rx::MoneroPowData,
randomx_factory::RandomXFactory,
sha3_difficulty,
sha3x_difficulty,
AchievedTargetDifficulty,
Difficulty,
PowAlgorithm,
Expand Down Expand Up @@ -178,7 +178,7 @@ pub fn check_target_difficulty(
) -> Result<AchievedTargetDifficulty, ValidationError> {
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) {
Expand Down
4 changes: 2 additions & 2 deletions base_layer/core/src/validation/mocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -116,7 +116,7 @@ impl<B: BlockchainBackend> HeaderValidation<B> for MockValidator {
_: &DifficultyCalculator,
) -> Result<AchievedTargetDifficulty, ValidationError> {
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)
Expand Down
4 changes: 2 additions & 2 deletions base_layer/core/tests/helpers/block_builders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -540,7 +540,7 @@ pub fn generate_new_block_with_coinbase<B: BlockchainBackend>(
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 {
Expand Down
14 changes: 7 additions & 7 deletions base_layer/tari_mining_helper_ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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()
}

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
}
Expand All @@ -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 = 1368783905506569398;
const DIFFICULTY: Difficulty = Difficulty::from_u64(3549);
unsafe {
let mut error = -1;
let error_ptr = &mut error as *mut c_int;
Expand Down

0 comments on commit a580103

Please sign in to comment.