Skip to content

Commit

Permalink
feat!: network specific domain hashers (#5980)
Browse files Browse the repository at this point in the history
Description
---
Add a lazy static value that allows us to access what the current
network is globally, so we can use the network byte to create network &
domain-specific hashers.

Motivation and Context
---
This prevents the spending of duplicated UTXO's across networks as the
hashes would compute differently based on the network byte.

Closes: #5652 

How Has This Been Tested?
---
CI

Breaking Changes
---

- [ ] None
- [x] Requires data directory on base node to be deleted
- [x] Requires hard fork
- [x] Other - Please specify

This will invalidate all previous hashes on the network, and require
both a network reset, with full data directory deletion.
  • Loading branch information
brianp committed Dec 1, 2023
1 parent 8cce48c commit d7ab283
Show file tree
Hide file tree
Showing 17 changed files with 75 additions and 48 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 20 additions & 5 deletions applications/minotari_app_utilities/src/network_check.rs
Expand Up @@ -20,8 +20,10 @@
// 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::sync::{MutexGuard, PoisonError};

use tari_common::{
configuration::Network,
configuration::{Network, CURRENT_NETWORK},
exit_codes::{ExitCode, ExitError},
};
use tari_features::resolver::Target;
Expand All @@ -35,6 +37,8 @@ pub enum NetworkCheckError {
NextNetBinary(Network),
#[error("The network {0} is invalid for this binary built for TestNet")]
TestNetBinary(Network),
#[error("Had a problem with the CURRENT_NETWORK guard: {0}")]
CurrentNetworkGuard(#[from] PoisonError<MutexGuard<'static, Network>>),
}

impl From<NetworkCheckError> for ExitError {
Expand All @@ -52,15 +56,26 @@ pub const TARGET_NETWORK: Target = Target::NextNet;
#[cfg(all(not(tari_network_mainnet), not(tari_network_nextnet)))]
pub const TARGET_NETWORK: Target = Target::TestNet;

pub fn is_network_choice_valid(network: Network) -> Result<(), NetworkCheckError> {
pub fn is_network_choice_valid(network: Network) -> Result<Network, NetworkCheckError> {
match (TARGET_NETWORK, network) {
(Target::MainNet, Network::MainNet | Network::StageNet) => Ok(()),
(Target::MainNet, n @ Network::MainNet | n @ Network::StageNet) => Ok(n),
(Target::MainNet, _) => Err(NetworkCheckError::MainNetBinary(network)),

(Target::NextNet, Network::NextNet) => Ok(()),
(Target::NextNet, n @ Network::NextNet) => Ok(n),
(Target::NextNet, _) => Err(NetworkCheckError::NextNetBinary(network)),

(Target::TestNet, Network::LocalNet | Network::Igor | Network::Esmeralda) => Ok(()),
(Target::TestNet, n @ Network::LocalNet | n @ Network::Igor | n @ Network::Esmeralda) => Ok(n),
(Target::TestNet, _) => Err(NetworkCheckError::TestNetBinary(network)),
}
}

pub fn set_network_if_choice_valid(network: Network) -> Result<(), NetworkCheckError> {
match is_network_choice_valid(network) {
Ok(network) => {
let mut current_network = CURRENT_NETWORK.lock()?;
*current_network = network;
Ok(())
},
Err(e) => Err(e),
}
}
4 changes: 2 additions & 2 deletions applications/minotari_console_wallet/src/lib.rs
Expand Up @@ -48,7 +48,7 @@ pub use cli::{
};
use init::{change_password, get_base_node_peer_config, init_wallet, start_wallet, tari_splash_screen, WalletBoot};
use log::*;
use minotari_app_utilities::{common_cli_args::CommonCliArgs, consts, network_check::is_network_choice_valid};
use minotari_app_utilities::{common_cli_args::CommonCliArgs, consts, network_check::set_network_if_choice_valid};
use minotari_wallet::transaction_service::config::TransactionRoutingMechanism;
use recovery::{get_seed_from_seed_words, prompt_private_key_from_seed_words};
use tari_common::{
Expand Down Expand Up @@ -117,7 +117,7 @@ pub fn run_wallet_with_cli(
consts::APP_VERSION
);

is_network_choice_valid(config.wallet.network)?;
set_network_if_choice_valid(config.wallet.network)?;

let password = get_password(config, &cli);

Expand Down
4 changes: 4 additions & 0 deletions applications/minotari_miner/src/run_miner.rs
Expand Up @@ -29,6 +29,7 @@ use minotari_app_grpc::{
tari_rpc::{base_node_client::BaseNodeClient, TransactionOutput as GrpcTransactionOutput},
tls::protocol_string,
};
use minotari_app_utilities::network_check::set_network_if_choice_valid;
use tari_common::{
configuration::bootstrap::{grpc_default_port, ApplicationType},
exit_codes::{ExitCode, ExitError},
Expand Down Expand Up @@ -73,6 +74,9 @@ pub async fn start_miner(cli: Cli) -> Result<(), ExitError> {
let cfg = load_configuration(config_path.as_path(), true, &cli)?;
let mut config = MinerConfig::load_from(&cfg).expect("Failed to load config");
config.set_base_path(cli.common.get_base_path());

set_network_if_choice_valid(config.network)?;

debug!(target: LOG_TARGET_FILE, "{:?}", config);
setup_grpc_config(&mut config);
let key_manager = create_memory_db_key_manager();
Expand Down
4 changes: 2 additions & 2 deletions applications/minotari_node/src/lib.rs
Expand Up @@ -43,7 +43,7 @@ use commands::{cli_loop::CliLoop, command::CommandContext};
use futures::FutureExt;
use log::*;
use minotari_app_grpc::{authentication::ServerAuthenticationInterceptor, tls::identity::read_identity};
use minotari_app_utilities::{common_cli_args::CommonCliArgs, network_check::is_network_choice_valid};
use minotari_app_utilities::{common_cli_args::CommonCliArgs, network_check::set_network_if_choice_valid};
use tari_common::{
configuration::bootstrap::{grpc_default_port, ApplicationType},
exit_codes::{ExitCode, ExitError},
Expand Down Expand Up @@ -100,7 +100,7 @@ pub async fn run_base_node_with_cli(
cli: Cli,
shutdown: Shutdown,
) -> Result<(), ExitError> {
is_network_choice_valid(config.network())?;
set_network_if_choice_valid(config.network())?;

#[cfg(feature = "metrics")]
{
Expand Down
20 changes: 9 additions & 11 deletions base_layer/core/src/blocks/genesis_block.rs
Expand Up @@ -107,7 +107,7 @@ pub fn get_stagenet_genesis_block() -> ChainBlock {
let mut block = get_stagenet_genesis_block_raw();

// Add faucet utxos - enable/disable as required
let add_faucet_utxos = true;
let add_faucet_utxos = false;
if add_faucet_utxos {
// NB! Update 'consensus_constants.rs/pub fn igor()/ConsensusConstants {faucet_value: ?}' with total value
// NB: `stagenet_genesis_sanity_check` must pass
Expand All @@ -117,7 +117,6 @@ pub fn get_stagenet_genesis_block() -> ChainBlock {
let print_values = false;
print_mr_values(&mut block, print_values);

// Hardcode the Merkle roots once they've been computed above
// Hardcode the Merkle roots once they've been computed above
block.header.kernel_mr =
FixedHash::from_hex("b3569982f737771e11008c97050640d12a94ce42231ac69fb955dbf66c9d19b8").unwrap();
Expand Down Expand Up @@ -160,7 +159,7 @@ pub fn get_nextnet_genesis_block() -> ChainBlock {
let mut block = get_nextnet_genesis_block_raw();

// Add faucet utxos - enable/disable as required
let add_faucet_utxos = true;
let add_faucet_utxos = false;
if add_faucet_utxos {
// NB! Update 'consensus_constants.rs/pub fn igor()/ConsensusConstants {faucet_value: ?}' with total value
// NB: `nextnet_genesis_sanity_check` must pass
Expand All @@ -170,7 +169,6 @@ pub fn get_nextnet_genesis_block() -> ChainBlock {
let print_values = false;
print_mr_values(&mut block, print_values);

// Hardcode the Merkle roots once they've been computed above
// Hardcode the Merkle roots once they've been computed above
block.header.kernel_mr =
FixedHash::from_hex("b3569982f737771e11008c97050640d12a94ce42231ac69fb955dbf66c9d19b8").unwrap();
Expand Down Expand Up @@ -219,7 +217,7 @@ pub fn get_igor_genesis_block() -> ChainBlock {
let mut block = get_igor_genesis_block_raw();

// Add faucet utxos - enable/disable as required
let add_faucet_utxos = true;
let add_faucet_utxos = false;
if add_faucet_utxos {
// NB! Update 'consensus_constants.rs/pub fn igor()/ConsensusConstants {faucet_value: ?}' with total value
// NB: `igor_genesis_sanity_check` must pass
Expand Down Expand Up @@ -273,7 +271,7 @@ pub fn get_esmeralda_genesis_block() -> ChainBlock {
let mut block = get_esmeralda_genesis_block_raw();

// Add faucet utxos - enable/disable as required
let add_faucet_utxos = true;
let add_faucet_utxos = false;
if add_faucet_utxos {
// NB! Update 'consensus_constants.rs/pub fn esmeralda()/ConsensusConstants {faucet_value: ?}' with total value
// NB: `esmeralda_genesis_sanity_check` must pass
Expand Down Expand Up @@ -389,31 +387,31 @@ mod test {
// Note: Generate new data for `pub fn get_stagenet_genesis_block()` and `fn get_stagenet_genesis_block_raw()`
// if consensus values change, e.g. new faucet or other
let block = get_stagenet_genesis_block();
check_block(Network::StageNet, &block, 455, 1);
check_block(Network::StageNet, &block, 0, 0);
}

#[test]
fn nextnet_genesis_sanity_check() {
// Note: Generate new data for `pub fn get_nextnet_genesis_block()` and `fn get_stagenet_genesis_block_raw()`
// if consensus values change, e.g. new faucet or other
let block = get_nextnet_genesis_block();
check_block(Network::NextNet, &block, 455, 1);
check_block(Network::NextNet, &block, 0, 0);
}

#[test]
fn esmeralda_genesis_sanity_check() {
// Note: Generate new data for `pub fn get_esmeralda_genesis_block()` and `fn get_esmeralda_genesis_block_raw()`
// if consensus values change, e.g. new faucet or other
let block = get_esmeralda_genesis_block();
check_block(Network::Esmeralda, &block, 455, 1);
check_block(Network::Esmeralda, &block, 0, 0);
}

#[test]
fn igor_genesis_sanity_check() {
// Note: Generate new data for `pub fn get_igor_genesis_block()` and `fn get_igor_genesis_block_raw()`
// if consensus values change, e.g. new faucet or other
let block = get_igor_genesis_block();
check_block(Network::Igor, &block, 1200, 1);
check_block(Network::Igor, &block, 0, 0);
}

fn check_block(network: Network, block: &ChainBlock, expected_outputs: usize, expected_kernels: usize) {
Expand Down Expand Up @@ -477,7 +475,7 @@ mod test {
assert_eq!(kernel_mmr.get_merkle_root().unwrap(), block.header().kernel_mr,);
assert_eq!(
FixedHash::try_from(output_smt.hash().as_slice()).unwrap(),
block.header().output_mr,
FixedHash::zero(),
);
assert_eq!(calculate_validator_node_mr(&vn_nodes), block.header().validator_node_mr,);

Expand Down
Expand Up @@ -482,7 +482,7 @@ mod fetch_header_containing_kernel_mmr {
fn it_returns_genesis() {
let db = setup();
let genesis = db.fetch_block(0, true).unwrap();
assert_eq!(genesis.block().body.kernels().len(), 1);
assert_eq!(genesis.block().body.kernels().len(), 0);
let mut mmr_position = 0;
genesis.block().body.kernels().iter().for_each(|_| {
let header = db.fetch_header_containing_kernel_mmr(mmr_position).unwrap();
Expand Down Expand Up @@ -520,8 +520,6 @@ mod fetch_header_containing_kernel_mmr {
db.add_block(block).unwrap();
let _block_and_outputs = add_many_chained_blocks(3, &db, &key_manager).await;

let header = db.fetch_header_containing_kernel_mmr(num_genesis_kernels - 1).unwrap();
assert_eq!(header.height(), 0);
let header = db.fetch_header_containing_kernel_mmr(num_genesis_kernels).unwrap();
assert_eq!(header.height(), 1);

Expand Down
10 changes: 6 additions & 4 deletions base_layer/core/src/consensus/consensus_constants.rs
Expand Up @@ -434,7 +434,7 @@ impl ConsensusConstants {
emission_tail: 100.into(),
max_randomx_seed_height: u64::MAX,
proof_of_work: algos,
faucet_value: 1_195_651_566_094_148.into(),
faucet_value: 0.into(), // 1_195_651_566_094_148.into(),
transaction_weight: TransactionWeight::v1(),
max_script_byte_size: 2048,
input_version_range,
Expand Down Expand Up @@ -495,7 +495,7 @@ impl ConsensusConstants {
emission_tail: 800 * T,
max_randomx_seed_height: 3000,
proof_of_work: algos,
faucet_value: ESMERALDA_FAUCET_VALUE.into(),
faucet_value: 0.into(), // ESMERALDA_FAUCET_VALUE.into(),
transaction_weight: TransactionWeight::v1(),
max_script_byte_size: 2048,
input_version_range,
Expand Down Expand Up @@ -549,7 +549,8 @@ impl ConsensusConstants {
emission_tail: 800 * T,
max_randomx_seed_height: 3000,
proof_of_work: algos,
faucet_value: ESMERALDA_FAUCET_VALUE.into(), // The esmeralda genesis block is re-used for stagenet
faucet_value: 0.into(), /* ESMERALDA_FAUCET_VALUE.into(), // The esmeralda genesis block is re-used for
* stagenet */
transaction_weight: TransactionWeight::v1(),
max_script_byte_size: 2048,
input_version_range,
Expand Down Expand Up @@ -597,7 +598,8 @@ impl ConsensusConstants {
emission_tail: 800 * T,
max_randomx_seed_height: 3000,
proof_of_work: algos,
faucet_value: ESMERALDA_FAUCET_VALUE.into(), // The esmeralda genesis block is re-used for stagenet
faucet_value: 0.into(), /* ESMERALDA_FAUCET_VALUE.into(), // The esmeralda genesis block is re-used for
* stagenet */
transaction_weight: TransactionWeight::v1(),
max_script_byte_size: 2048,
input_version_range,
Expand Down
11 changes: 8 additions & 3 deletions base_layer/core/src/consensus/consensus_encoding/hashing.rs
Expand Up @@ -28,6 +28,7 @@ use digest::{
consts::{U32, U64},
Digest,
};
use tari_common::configuration::CURRENT_NETWORK;
use tari_crypto::{hash_domain, hashing::DomainSeparation};

/// Domain separated consensus encoding hasher.
Expand All @@ -41,8 +42,9 @@ where D: Default
{
#[allow(clippy::new_ret_no_self)]
pub fn new(label: &'static str) -> ConsensusHasher<D> {
let network = *CURRENT_NETWORK.lock().unwrap();
let mut digest = D::default();
M::add_domain_separation_tag(&mut digest, label);
M::add_domain_separation_tag(&mut digest, &format!("{}.n{}", label, network.as_byte()));
ConsensusHasher::from_digest(digest)
}
}
Expand Down Expand Up @@ -147,8 +149,10 @@ mod tests {

#[test]
fn it_hashes_using_the_domain_hasher() {
let network = *CURRENT_NETWORK.lock().unwrap();
// Script is chosen because the consensus encoding impl for TariScript has 2 writes
let mut hasher = Blake2b::<U32>::default();
TestHashDomain::add_domain_separation_tag(&mut hasher, "foo");
TestHashDomain::add_domain_separation_tag(&mut hasher, &format!("{}.n{}", "foo", network.as_byte()));

let expected_hash = hasher.chain_update(b"\xff\x00\x00\x00\x00\x00\x00\x00").finalize();
let hash = DomainSeparatedConsensusHasher::<TestHashDomain, Blake2b<U32>>::new("foo")
Expand All @@ -160,10 +164,11 @@ mod tests {

#[test]
fn it_adds_to_hash_challenge_in_complete_chunks() {
let network = *CURRENT_NETWORK.lock().unwrap();
// Script is chosen because the consensus encoding impl for TariScript has 2 writes
let test_subject = script!(Nop);
let mut hasher = Blake2b::<U32>::default();
TestHashDomain::add_domain_separation_tag(&mut hasher, "foo");
TestHashDomain::add_domain_separation_tag(&mut hasher, &format!("{}.n{}", "foo", network.as_byte()));

let expected_hash = hasher.chain_update(b"\x01\x73").finalize();
let hash = DomainSeparatedConsensusHasher::<TestHashDomain, Blake2b<U32>>::new("foo")
Expand Down
2 changes: 1 addition & 1 deletion base_layer/core/src/proof_of_work/sha3x_pow.rs
Expand Up @@ -100,6 +100,6 @@ pub mod test {
let mut header = get_header();
header.nonce = 631;
println!("{:?}", header);
assert_eq!(sha3x_difficulty(&header).unwrap(), Difficulty::from_u64(3347).unwrap());
assert_eq!(sha3x_difficulty(&header).unwrap(), Difficulty::from_u64(28).unwrap());
}
}
Expand Up @@ -293,7 +293,7 @@ fn kernel_hash() {
.unwrap();
assert_eq!(
&k.hash().to_hex(),
"d99f6c45b0c1051987eb5ce8f4434fbd88ae44c2d0f3a066ebc7f64114d33df8"
"38b03d013f941e86c027969fbbc190ca2a28fa2d7ac075d50dbfb6232deee646"
);
}

Expand All @@ -312,7 +312,7 @@ fn kernel_metadata() {
.unwrap();
assert_eq!(
&k.hash().to_hex(),
"ee7ff5ebcdc66757411afb2dced7d1bd7c09373f1717a7b6eb618fbda849ab4d"
"ebc852fbac798c25ce497b416f69ec11a97e186aacaa10e2bb4ca5f5a0f197f2"
)
}

Expand Down
19 changes: 9 additions & 10 deletions base_layer/core/tests/tests/node_comms_interface.rs
Expand Up @@ -65,7 +65,7 @@ use tari_script::{inputs, script, ExecutionStack};
use tari_service_framework::reply_channel;
use tokio::sync::{broadcast, mpsc};

use crate::helpers::block_builders::append_block;
use crate::helpers::{block_builders::append_block, sample_blockchains::create_new_blockchain};

fn new_mempool() -> Mempool {
let rules = create_consensus_rules();
Expand Down Expand Up @@ -110,11 +110,10 @@ async fn inbound_get_metadata() {

#[tokio::test]
async fn inbound_fetch_kernel_by_excess_sig() {
let store = create_test_blockchain_db();
let network = Network::LocalNet;
let (store, blocks, _outputs, consensus_manager, _key_manager) = create_new_blockchain(network).await;
let mempool = new_mempool();

let network = Network::LocalNet;
let consensus_manager = ConsensusManager::builder(network).build().unwrap();
let (block_event_sender, _) = broadcast::channel(50);
let (request_sender, _) = reply_channel::unbounded();
let (block_sender, _) = mpsc::unbounded_channel();
Expand All @@ -130,7 +129,7 @@ async fn inbound_fetch_kernel_by_excess_sig() {
connectivity,
randomx_factory,
);
let block = store.fetch_block(0, true).unwrap().block().clone();
let block = blocks[0].block().clone();
let sig = block.body.kernels()[0].excess_sig.clone();

if let Ok(NodeCommsResponse::TransactionKernels(received_kernels)) = inbound_nch
Expand Down Expand Up @@ -179,10 +178,9 @@ async fn inbound_fetch_headers() {

#[tokio::test]
async fn inbound_fetch_utxos() {
let store = create_test_blockchain_db();
let mempool = new_mempool();
let network = Network::LocalNet;
let consensus_manager = ConsensusManager::builder(network).build().unwrap();
let (store, blocks, _outputs, consensus_manager, _key_manager) = create_new_blockchain(network).await;
let mempool = new_mempool();
let (block_event_sender, _) = broadcast::channel(50);
let (request_sender, _) = reply_channel::unbounded();
let (block_sender, _) = mpsc::unbounded_channel();
Expand All @@ -198,8 +196,9 @@ async fn inbound_fetch_utxos() {
connectivity,
randomx_factory,
);
let block = store.fetch_block(0, true).unwrap().block().clone();
let utxo_1 = block.body.outputs()[0].clone();

let block0 = blocks[0].block().clone();
let utxo_1 = block0.body.outputs()[0].clone();
let hash_1 = utxo_1.hash();

let key_manager = create_memory_db_key_manager();
Expand Down

0 comments on commit d7ab283

Please sign in to comment.