Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: change how sha3 difficulty is calculated #4528

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions applications/tari_base_node/src/grpc/base_node_grpc_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ impl tari_rpc::base_node_server::BaseNode for BaseNodeGrpcServer {
};
// construct response
let block_hash = new_block.hash();
let mining_hash = new_block.header.merged_mining_hash().to_vec();
let mining_hash = new_block.header.mining_hash().to_vec();
let block: Option<tari_rpc::Block> = Some(
new_block
.try_into()
Expand Down Expand Up @@ -587,7 +587,7 @@ impl tari_rpc::base_node_server::BaseNode for BaseNodeGrpcServer {
};
// construct response
let block_hash = new_block.hash();
let mining_hash = new_block.header.merged_mining_hash().to_vec();
let mining_hash = new_block.header.mining_hash().to_vec();

let (header, block_body) = new_block.into_header_body();
let mut header_bytes = Vec::new();
Expand Down
80 changes: 26 additions & 54 deletions applications/tari_miner/src/difficulty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,108 +20,80 @@
// 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::convert::TryInto;

use sha3::{Digest, Sha3_256};
use tari_app_grpc::tari_rpc::BlockHeader;
use tari_core::large_ints::U256;
use tari_utilities::ByteArray;
use tari_app_grpc::tari_rpc::BlockHeader as grpc_header;
use tari_core::{blocks::BlockHeader, large_ints::U256};
use tari_utilities::epoch_time::EpochTime;

use crate::errors::{err_empty, MinerError};
use crate::errors::MinerError;

pub type Difficulty = u64;

#[derive(Clone)]
pub struct BlockHeaderSha3 {
header: BlockHeader,
pow_bytes: Vec<u8>,
hash_before_timestamp: Sha3_256,
pub timestamp: u64,
pub nonce: u64,
pub header: BlockHeader,
hash_merge_mining: Sha3_256,
pub hashes: u64,
}

impl BlockHeaderSha3 {
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
pub fn new(header: BlockHeader) -> Result<Self, MinerError> {
use std::convert::TryFrom;

use tari_core::proof_of_work::ProofOfWork; // this is only dep left on tari_code
pub fn new(header: grpc_header) -> Result<Self, MinerError> {
let header: BlockHeader = header.try_into().map_err(MinerError::BlockHeader)?;

// Not stressing about efficiency here as it will change soon
let pow = ProofOfWork::try_from(header.pow.clone().ok_or_else(|| err_empty("header.pow"))?)
.map_err(MinerError::BlockHeader)?;
let timestamp = header.timestamp.as_ref().ok_or_else(|| err_empty("header.timestamp"))?;
let hash_before_timestamp = Sha3_256::new()
.chain((header.version as u16).to_le_bytes())
.chain(header.height.to_le_bytes())
.chain(header.prev_hash.as_bytes());
let hash_merge_mining = Sha3_256::new().chain(header.mining_hash());

Ok(Self {
pow_bytes: pow.to_bytes(),
hash_before_timestamp,
timestamp: timestamp.seconds as u64,
nonce: header.nonce,
hash_merge_mining,
header,
hashes: 0,
})
}

#[inline]
fn get_hash_before_nonce(&self) -> Sha3_256 {
self.hash_before_timestamp
.clone()
.chain(self.timestamp.to_le_bytes())
.chain(self.header.input_mr.as_bytes())
.chain(self.header.output_mr.as_bytes())
.chain(self.header.output_mmr_size.to_le_bytes())
.chain(self.header.witness_mr.as_bytes())
.chain(self.header.kernel_mr.as_bytes())
.chain(self.header.kernel_mmr_size.to_le_bytes())
.chain(self.header.total_kernel_offset.as_bytes())
.chain(self.header.total_script_offset.as_bytes())
self.hash_merge_mining.clone()
}

/// This function will update the timestamp of the header, but only if the new timestamp is greater than the current
/// one.
pub fn set_forward_timestamp(&mut self, timestamp: u64) {
// if the timestamp has been advanced by the base_node due to the median time we should not reverse it but we
// should only change the timestamp if we move it forward.
if timestamp > self.timestamp {
self.timestamp = timestamp;
if timestamp > self.header.timestamp.as_u64() {
self.header.timestamp = EpochTime::from(timestamp);
self.hash_merge_mining = Sha3_256::new().chain(self.header.mining_hash());
}
}

pub fn random_nonce(&mut self) {
use rand::{rngs::OsRng, RngCore};
self.nonce = OsRng.next_u64();
self.header.nonce = OsRng.next_u64();
}

#[inline]
pub fn inc_nonce(&mut self) {
self.nonce = self.nonce.wrapping_add(1);
self.header.nonce = self.header.nonce.wrapping_add(1);
}

#[inline]
pub fn difficulty(&mut self) -> Difficulty {
self.hashes = self.hashes.saturating_add(1);
let hash = self
.get_hash_before_nonce()
.chain(self.nonce.to_le_bytes())
.chain(&self.pow_bytes)
.chain(self.header.nonce.to_le_bytes())
.chain(self.header.pow.to_bytes())
.finalize();
let hash = Sha3_256::digest(&hash);
big_endian_difficulty(&hash)
}

#[allow(clippy::cast_possible_wrap)]
pub fn create_header(&self) -> BlockHeader {
let mut header = self.header.clone();
header.timestamp = Some(prost_types::Timestamp {
seconds: self.timestamp as i64,
nanos: 0,
});
header.nonce = self.nonce;
header
pub fn create_header(&self) -> grpc_header {
self.header.clone().into()
}

#[inline]
Expand All @@ -140,13 +112,13 @@ fn big_endian_difficulty(hash: &[u8]) -> Difficulty {
#[cfg(test)]
pub mod test {
use chrono::{DateTime, NaiveDate, Utc};
use tari_core::{blocks::BlockHeader as CoreBlockHeader, proof_of_work::sha3_difficulty as core_sha3_difficulty};
use tari_core::proof_of_work::sha3_difficulty as core_sha3_difficulty;

use super::*;

#[allow(clippy::cast_sign_loss)]
pub fn get_header() -> (BlockHeader, CoreBlockHeader) {
let mut header = CoreBlockHeader::new(0);
pub fn get_header() -> (grpc_header, BlockHeader) {
let mut header = BlockHeader::new(0);
header.timestamp =
(DateTime::<Utc>::from_utc(NaiveDate::from_ymd(2000, 1, 1).and_hms(1, 1, 1), Utc).timestamp() as u64)
.into();
Expand All @@ -165,7 +137,7 @@ pub mod test {
hasher.difficulty(),
core_sha3_difficulty(&core_header).as_u64(),
"with nonces = {}:{}",
hasher.nonce,
hasher.header.nonce,
core_header.nonce
);
core_header.nonce += 1;
Expand Down
8 changes: 4 additions & 4 deletions applications/tari_miner/src/miner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,15 +188,15 @@ pub fn mining_task(
if difficulty >= target_difficulty {
debug!(
target: LOG_TARGET,
"Miner {} found nonce {} with matching difficulty {}", miner, hasher.nonce, difficulty
"Miner {} found nonce {} with matching difficulty {}", miner, hasher.header.nonce, difficulty
);
if let Err(err) = sender.try_send(MiningReport {
miner,
difficulty,
hashes: hasher.hashes,
elapsed: start.elapsed(),
height: hasher.height(),
last_nonce: hasher.nonce,
last_nonce: hasher.header.nonce,
header: Some(hasher.create_header()),
target_difficulty,
}) {
Expand All @@ -212,14 +212,14 @@ pub fn mining_task(
return;
}
}
if hasher.nonce % REPORTING_FREQUENCY == 0 {
if hasher.header.nonce % REPORTING_FREQUENCY == 0 {
let res = sender.try_send(MiningReport {
miner,
difficulty,
hashes: hasher.hashes,
elapsed: start.elapsed(),
header: None,
last_nonce: hasher.nonce,
last_nonce: hasher.header.nonce,
height: hasher.height(),
target_difficulty,
});
Expand Down
6 changes: 3 additions & 3 deletions base_layer/core/src/blocks/block_header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,9 +215,9 @@ impl BlockHeader {
}
}

/// Provides a hash of the header, used for the merge mining.
/// Provides a mining hash of the header, used for the mining.
/// This differs from the normal hash by not hashing the nonce and kernel pow.
pub fn merged_mining_hash(&self) -> FixedHash {
pub fn mining_hash(&self) -> FixedHash {
DomainSeparatedConsensusHasher::<BlocksHashDomain>::new("block_header")
.chain(&self.version)
.chain(&self.height)
Expand Down Expand Up @@ -277,7 +277,7 @@ impl From<NewBlockHeaderTemplate> for BlockHeader {
impl Hashable for BlockHeader {
fn hash(&self) -> Vec<u8> {
DomainSeparatedConsensusHasher::<BlocksHashDomain>::new("block_header")
.chain(&self.merged_mining_hash().as_slice())
.chain(&self.mining_hash().as_slice())
.chain(&self.pow)
.chain(&self.nonce)
.finalize()
Expand Down
10 changes: 5 additions & 5 deletions base_layer/core/src/proof_of_work/monero_rx/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ fn get_random_x_difficulty(input: &[u8], vm: &RandomXVMInstance) -> Result<(Diff
/// If these assertions pass, a valid `MoneroPowData` instance is returned
fn verify_header(header: &BlockHeader) -> Result<MoneroPowData, MergeMineError> {
let monero_data = MoneroPowData::from_header(header)?;
let expected_merge_mining_hash = header.merged_mining_hash();
let expected_merge_mining_hash = header.mining_hash();

// Check that the Tari MM hash is found in the monero coinbase transaction
let is_found = monero_data.coinbase_tx.prefix.extra.0.iter().any(|item| match item {
Expand Down Expand Up @@ -308,7 +308,7 @@ mod test {
nonce: 0,
pow: ProofOfWork::default(),
};
let hash = block_header.merged_mining_hash();
let hash = block_header.mining_hash();
append_merge_mining_tag(&mut block, hash).unwrap();
let hashes = create_ordered_transaction_hashes_from_block(&block);
assert_eq!(hashes.len(), block.tx_hashes.len() + 1);
Expand Down Expand Up @@ -364,7 +364,7 @@ mod test {
nonce: 0,
pow: ProofOfWork::default(),
};
let hash = block_header.merged_mining_hash();
let hash = block_header.mining_hash();
append_merge_mining_tag(&mut block, hash).unwrap();
let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap());
let mut hashes = Vec::with_capacity(count as usize);
Expand Down Expand Up @@ -522,7 +522,7 @@ mod test {
nonce: 0,
pow: ProofOfWork::default(),
};
let hash = block_header.merged_mining_hash();
let hash = block_header.mining_hash();
append_merge_mining_tag(&mut block, hash).unwrap();
let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap());
let mut hashes = Vec::with_capacity(count as usize);
Expand Down Expand Up @@ -615,7 +615,7 @@ mod test {
nonce: 0,
pow: ProofOfWork::default(),
};
let hash = block_header.merged_mining_hash();
let hash = block_header.mining_hash();
append_merge_mining_tag(&mut block, hash).unwrap();
let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap());
let mut hashes = Vec::with_capacity(count as usize);
Expand Down
16 changes: 2 additions & 14 deletions base_layer/core/src/proof_of_work/sha3_pow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use sha3::{Digest, Sha3_256};
use tari_utilities::ByteArray;

use crate::{
blocks::BlockHeader,
Expand All @@ -39,18 +38,7 @@ pub fn sha3_difficulty(header: &BlockHeader) -> Difficulty {

pub fn sha3_hash(header: &BlockHeader) -> Vec<u8> {
Sha3_256::new()
.chain(header.version.to_le_bytes())
.chain(header.height.to_le_bytes())
.chain(header.prev_hash.as_bytes())
.chain(header.timestamp.as_u64().to_le_bytes())
.chain(header.input_mr.as_bytes())
.chain(header.output_mr.as_bytes())
.chain(header.output_mmr_size.to_le_bytes())
.chain(header.witness_mr.as_bytes())
.chain(header.kernel_mr.as_bytes())
.chain(header.kernel_mmr_size.to_le_bytes())
.chain(header.total_kernel_offset.as_bytes())
.chain(header.total_script_offset.as_bytes())
.chain(header.mining_hash())
.chain(header.nonce.to_le_bytes())
.chain(header.pow.to_bytes())
.finalize()
Expand Down Expand Up @@ -101,6 +89,6 @@ pub mod test {
fn validate_max_target() {
let mut header = get_header();
header.nonce = 1;
assert_eq!(sha3_difficulty(&header), Difficulty::from(1));
assert_eq!(sha3_difficulty(&header), Difficulty::from(3));
}
}
2 changes: 1 addition & 1 deletion base_layer/core/tests/block_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ 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::<MoneroBlock>(&bytes[..]).unwrap();
let hash = tblock.header.merged_mining_hash();
let hash = tblock.header.mining_hash();
monero_rx::append_merge_mining_tag(&mut mblock, hash).unwrap();
let hashes = monero_rx::create_ordered_transaction_hashes_from_block(&mblock);
let merkle_root = monero_rx::tree_hash(&hashes).unwrap();
Expand Down
4 changes: 2 additions & 2 deletions base_layer/tari_mining_helper_ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,8 +371,8 @@ mod tests {

#[test]
fn detect_change_in_consensus_encoding() {
const NONCE: u64 = 17497411907229199779;
const DIFFICULTY: Difficulty = Difficulty::from_u64(1984);
const NONCE: u64 = 5714152803266684615;
const DIFFICULTY: Difficulty = Difficulty::from_u64(1565);
unsafe {
let mut error = -1;
let error_ptr = &mut error as *mut c_int;
Expand Down
2 changes: 2 additions & 0 deletions base_layer/wallet_ffi/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -2921,6 +2921,8 @@ TariPublicKey *wallet_get_public_key(struct TariWallet *wallet,
* `script_private_key` - Tari script private key, k_S, is used to create the script signature
* `covenant` - The covenant that will be executed when spending this output
* `message` - The message that the transaction will have
* `encrypted_value` - Encrypted value.
* `minimum_value_promise` - The minimum value of the commitment that is proven by the range proof
* `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions
* as an out parameter.
*
Expand Down