use std::sync::Arc;
use chrono::{DateTime, FixedOffset};
use tari_common::configuration::Network;
use tari_common_types::types::{FixedHash, PrivateKey};
use tari_crypto::tari_utilities::hex::*;
use tari_node_components::blocks::{Block, BlockHeader, BlockHeaderAccumulatedData, ChainBlock};
use tari_transaction_components::{
aggregated_body::AggregateBody,
tari_proof_of_work::{PowAlgorithm, PowData, ProofOfWork},
transaction_components::{TransactionInput, TransactionKernel, TransactionOutput},
};
pub const VALIDATOR_MR_EMPTY_PLACEHOLDER_HASH: FixedHash = FixedHash::new([
0x27, 0x7d, 0xa6, 0x5c, 0x40, 0xb2, 0xcf, 0x99, 0xdb, 0x86, 0xba, 0xed, 0xb9, 0x03, 0xa3, 0xf0, 0xa3, 0x85, 0x40,
0xf3, 0xa9, 0x4d, 0x40, 0xc8, 0x26, 0xee, 0xca, 0xc7, 0xe2, 0x7d, 0x5d, 0xfc,
]);
pub fn get_genesis_block(network: Network) -> ChainBlock {
use Network::{Esmeralda, Igor, LocalNet, MainNet, NextNet, StageNet};
match network {
MainNet => get_mainnet_genesis_block(),
StageNet => get_stagenet_genesis_block(),
NextNet => get_nextnet_genesis_block(),
Igor => get_igor_genesis_block(),
Esmeralda => get_esmeralda_genesis_block(),
LocalNet => get_localnet_genesis_block(),
}
}
fn add_pre_mine_utxos_to_genesis_block(file: &str, block: &mut Block) {
let mut outputs = Vec::new();
let mut inputs = Vec::new();
for line in file.lines() {
if let Ok(output) = serde_json::from_str::<TransactionOutput>(line) {
outputs.push(output);
} else if let Ok(input) = serde_json::from_str::<TransactionInput>(line) {
inputs.push(input);
} else if let Ok(kernel) = serde_json::from_str::<TransactionKernel>(line) {
block.body.add_kernel(kernel);
block.header.kernel_mmr_size += 1;
} else if let Ok(excess) = serde_json::from_str::<PrivateKey>(line) {
block.header.total_kernel_offset = &block.header.total_kernel_offset + &excess;
} else {
panic!("Error: Could not deserialize line: {line} in file: {file}");
}
}
block.header.output_smt_size += outputs.len() as u64;
block.header.output_smt_size -= inputs.len() as u64;
block.body.add_outputs(outputs);
block.body.add_inputs(inputs);
block.body.sort();
}
pub fn get_stagenet_genesis_block() -> ChainBlock {
let mut block = get_stagenet_genesis_block_raw();
let add_pre_mine_utxos = false;
if add_pre_mine_utxos {
let file_contents = include_str!("pre_mine/stagenet_pre_mine.json");
add_pre_mine_utxos_to_genesis_block(file_contents, &mut block);
block.header.kernel_mr =
FixedHash::from_hex("a08ff15219beea81d4131465290443fb3bd99d28b8af85975dbb2c77cb4cb5a0").unwrap();
block.header.input_mr =
FixedHash::from_hex("212ce6f5f7fc67dcb73b2a8a7a11404703aca210a7c75de9e50d914c9f9942c2").unwrap();
block.header.output_mr =
FixedHash::from_hex("5350415253455f4d45524b4c455f504c414345484f4c4445525f484153485f5f").unwrap();
block.header.validator_node_mr = VALIDATOR_MR_EMPTY_PLACEHOLDER_HASH;
}
let accumulated_data = BlockHeaderAccumulatedData::genesis(block.hash(), block.header.total_kernel_offset.clone());
ChainBlock::try_construct(Arc::new(block), accumulated_data).unwrap()
}
fn get_stagenet_genesis_block_raw() -> Block {
let genesis_timestamp = DateTime::parse_from_rfc2822("11 Mar 2024 08:00:00 +0200").expect("parse may not fail");
let not_before_proof = b"i am the stagenet genesis block, watch out, here i come \
\
The New York Times , 2000/01/01 \
\
Lorem Ipsum \
\
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore \
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo \
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla \
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id \
est laborum.";
if not_before_proof.len() > PowData::default().max_size() {
panic!(
"Not-before-proof data is too large, exceeds limit by '{}' bytes",
not_before_proof.len() - PowData::default().max_size()
);
}
get_raw_block(&genesis_timestamp, &PowData::from_bytes_truncate(not_before_proof))
}
pub fn get_nextnet_genesis_block() -> ChainBlock {
let mut block = get_nextnet_genesis_block_raw();
let add_pre_mine_utxos = false;
if add_pre_mine_utxos {
let file_contents = include_str!("pre_mine/nextnet_pre_mine.json");
add_pre_mine_utxos_to_genesis_block(file_contents, &mut block);
block.header.kernel_mr =
FixedHash::from_hex("36881d87e25183f5189d2dca5f7da450c399e7006dafd9bd9240f73a5fb3f0ad").unwrap();
block.header.input_mr =
FixedHash::from_hex("212ce6f5f7fc67dcb73b2a8a7a11404703aca210a7c75de9e50d914c9f9942c2").unwrap();
block.header.output_mr =
FixedHash::from_hex("5350415253455f4d45524b4c455f504c414345484f4c4445525f484153485f5f").unwrap();
block.header.validator_node_mr = VALIDATOR_MR_EMPTY_PLACEHOLDER_HASH;
}
block.header.output_mr =
FixedHash::from_hex("5350415253455f4d45524b4c455f504c414345484f4c4445525f484153485f5f").unwrap();
let accumulated_data = BlockHeaderAccumulatedData::genesis(block.hash(), block.header.total_kernel_offset.clone());
ChainBlock::try_construct(Arc::new(block), accumulated_data).unwrap()
}
fn get_nextnet_genesis_block_raw() -> Block {
let genesis_timestamp = DateTime::parse_from_rfc2822("09 May 2025 08:00:00 +0200").expect("parse may not fail");
let not_before_proof = b"nextnet has a blast, its prowess echoed in every gust \
\
The New York Times , 2000/01/01 \
\
Lorem Ipsum \
\
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore \
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo \
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla \
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id \
est laborum.";
if not_before_proof.len() > PowData::default().max_size() {
panic!(
"Not-before-proof data is too large, exceeds limit by '{}' bytes",
not_before_proof.len() - PowData::default().max_size()
);
}
get_raw_block(&genesis_timestamp, &PowData::from_bytes_truncate(not_before_proof))
}
pub fn get_mainnet_genesis_block() -> ChainBlock {
let mut block = get_mainnet_genesis_block_raw();
let add_pre_mine_utxos = true;
if add_pre_mine_utxos {
let file_contents = include_str!("pre_mine/mainnet_pre_mine.json");
add_pre_mine_utxos_to_genesis_block(file_contents, &mut block);
block.header.kernel_mr =
FixedHash::from_hex("f73daf81a3672d9e290adecb77f6071c82b7095f34bfcdfcfafe8c2148b54fad").unwrap();
block.header.input_mr =
FixedHash::from_hex("b7b38b76f5832b5b63691a8334dfa67d8c762b77b2b4aa4f648c4eb1dfb25c1e").unwrap();
block.header.output_mr =
FixedHash::from_hex("ce7f66009cbd277968ba86d5e7be1b41aa268f5cb2f655ea1741cc82ab793c3b").unwrap();
block.header.block_output_mr =
FixedHash::from_hex("91e997520b0eee770914334692080f92d18db434d373561f8842c56d70c11b97").unwrap();
block.header.validator_node_mr = VALIDATOR_MR_EMPTY_PLACEHOLDER_HASH;
}
let accumulated_data = BlockHeaderAccumulatedData::genesis(block.hash(), block.header.total_kernel_offset.clone());
ChainBlock::try_construct(Arc::new(block), accumulated_data).unwrap()
}
fn get_mainnet_genesis_block_raw() -> Block {
let mut gen_block_payload = include_bytes!("gen_block/Tari.Manifesto").to_vec();
let genesis_timestamp = DateTime::parse_from_rfc2822("06 May 2025 08:00:00 +0200").expect("parse may not fail");
let soons_secrets = vec![
"e777pxxdrk32oj5t2g5hokt5n2slbiwxgcmmdhvgzrvvvdvlqnuyijbd.fvboe",
"1993f729e1584439e3cfb70d9bd8bab377103b37fbcd2a8d4ec38195601adf7a",
"597f70b6d2e02da4eaf98c7ea95404ba2d7830ec576fc81586cf8c49db941661",
"8b46b1969b9f879d8f2d31bebd5126be4ff807b297213837ab0d2ef59db46d6f",
"459a45beae1854f70918611b50e85bdbc0d21a688852192e96628cac04b92f22",
];
let not_before_proof = "c4e258f0fab1077188ae0792a4224183af070202bee11e3c752918a7e9b2be52";
for secret in soons_secrets {
let mut bytes = secret.as_bytes().to_vec();
gen_block_payload.append(&mut bytes);
}
let mut not_before_proof = not_before_proof.as_bytes().to_vec();
gen_block_payload.append(&mut not_before_proof);
if gen_block_payload.len() > PowData::default().max_size() {
panic!(
"Not-before-proof data is too large, exceeds limit by '{}' bytes",
gen_block_payload.len() - PowData::default().max_size()
);
}
let mut block = get_raw_block(&genesis_timestamp, &PowData::from_bytes_truncate(gen_block_payload));
block.header.nonce = 61724;
block
}
pub fn get_igor_genesis_block() -> ChainBlock {
let mut block = get_igor_genesis_block_raw();
let add_pre_mine_utxos = false;
if add_pre_mine_utxos {
let file_contents = include_str!("pre_mine/igor_pre_mine.json");
add_pre_mine_utxos_to_genesis_block(file_contents, &mut block);
block.header.kernel_mr =
FixedHash::from_hex("bc5d677b0b8349adc9d7e4a18ace7406986fc7017866f4fd351ecb0f35d6da5e").unwrap();
block.header.input_mr =
FixedHash::from_hex("0000000000000000000000000000000000000000000000000000000000000000").unwrap();
block.header.output_mr =
FixedHash::from_hex("5350415253455f4d45524b4c455f504c414345484f4c4445525f484153485f5f").unwrap();
block.header.validator_node_mr = VALIDATOR_MR_EMPTY_PLACEHOLDER_HASH;
}
let accumulated_data = BlockHeaderAccumulatedData::genesis(block.hash(), block.header.total_kernel_offset.clone());
ChainBlock::try_construct(Arc::new(block), accumulated_data).unwrap()
}
fn get_igor_genesis_block_raw() -> Block {
let genesis_timestamp = DateTime::parse_from_rfc2822("11 Mar 2024 08:00:00 +0200").expect("parse may not fail");
let not_before_proof = b"but igor is the best, it is whispered in the wind \
\
The New York Times , 2000/01/01 \
\
Lorem Ipsum \
\
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore \
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo \
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla \
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id \
est laborum.";
if not_before_proof.len() > PowData::default().max_size() {
panic!(
"Not-before-proof data is too large, exceeds limit by '{}' bytes",
not_before_proof.len() - PowData::default().max_size()
);
}
get_raw_block(&genesis_timestamp, &PowData::from_bytes_truncate(not_before_proof))
}
pub fn get_esmeralda_genesis_block() -> ChainBlock {
let mut block = get_esmeralda_genesis_block_raw();
let add_pre_mine_utxos = true;
if add_pre_mine_utxos {
let file_contents = include_str!("pre_mine/esmeralda_pre_mine.json");
add_pre_mine_utxos_to_genesis_block(file_contents, &mut block);
block.header.kernel_mr =
FixedHash::from_hex("91402b11319114845dd7ce5e5c191dab86f886147515437cb1549ec8c082060e").unwrap();
block.header.input_mr =
FixedHash::from_hex("16a4ad34eccac12cbafe3ab448ca2c0d0dfcccd23098667bc6530da30526fb3d").unwrap();
block.header.output_mr =
FixedHash::from_hex("a871470fefd60e1c268beeac5918c7997073e1bbd5c5890306acbfe57b85329f").unwrap();
block.header.validator_node_mr = VALIDATOR_MR_EMPTY_PLACEHOLDER_HASH;
block.header.block_output_mr =
FixedHash::from_hex("ab2dcfdfd29197c41838a3fd4fab24135578f741cc21614cdb575554c7513424").unwrap();
}
let accumulated_data = BlockHeaderAccumulatedData::genesis(block.hash(), block.header.total_kernel_offset.clone());
ChainBlock::try_construct(Arc::new(block), accumulated_data).unwrap()
}
fn get_esmeralda_genesis_block_raw() -> Block {
let genesis_timestamp = DateTime::parse_from_rfc2822("09 May 2025 08:00:00 +0200").expect("parse may not fail");
let not_before_proof =
b"as I sip my drink, thoughts of esmeralda consume my mind, like a refreshing nourishing draught \
\
The New York Times , 2000/01/01 \
\
Lorem Ipsum \
\
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore \
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo \
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla \
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id \
est laborum.";
if not_before_proof.len() > PowData::default().max_size() {
panic!(
"Not-before-proof data is too large, exceeds limit by '{}' bytes",
not_before_proof.len() - PowData::default().max_size()
);
}
get_raw_block(&genesis_timestamp, &PowData::from_bytes_truncate(not_before_proof))
}
pub fn get_localnet_genesis_block() -> ChainBlock {
let block = crate::blocks::genesis_block::get_localnet_genesis_block_raw();
let accumulated_data = BlockHeaderAccumulatedData::genesis(block.hash(), block.header.total_kernel_offset.clone());
ChainBlock::try_construct(Arc::new(block), accumulated_data).unwrap()
}
fn get_localnet_genesis_block_raw() -> Block {
let genesis_timestamp = DateTime::parse_from_rfc2822("20 Feb 2024 08:01:00 +0200").expect("parse may not fail");
let not_before_proof =
b"as I sip my drink, thoughts of esmeralda consume my mind, like a refreshing nourishing draught \
\
The New York Times , 2000/01/01 \
\
Lorem Ipsum \
\
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore \
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo \
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla \
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id \
est laborum.";
if not_before_proof.len() > PowData::default().max_size() {
panic!(
"Not-before-proof data is too large, exceeds limit by '{}' bytes",
not_before_proof.len() - PowData::default().max_size()
);
}
get_raw_block(&genesis_timestamp, &PowData::from_bytes_truncate(not_before_proof))
}
fn get_raw_block(genesis_timestamp: &DateTime<FixedOffset>, not_before_proof: &PowData) -> Block {
#[allow(clippy::cast_sign_loss)]
let timestamp = genesis_timestamp.timestamp() as u64;
Block {
header: BlockHeader {
version: 0,
height: 0,
prev_hash: FixedHash::zero(),
timestamp: timestamp.into(),
output_mr: FixedHash::from_hex("5350415253455f4d45524b4c455f504c414345484f4c4445525f484153485f5f").unwrap(),
block_output_mr: FixedHash::from_hex("622720a6571c33d6bf6138d9e737d3468c77f1193640698ad459953d24ec0812")
.unwrap(),
output_smt_size: 0,
kernel_mr: FixedHash::from_hex("c14803066909d6d22abf0d2d2782e8936afc3f713f2af3a4ef5c42e8400c1303").unwrap(),
kernel_mmr_size: 0,
validator_node_mr: VALIDATOR_MR_EMPTY_PLACEHOLDER_HASH,
validator_node_size: 0,
input_mr: FixedHash::from_hex("212ce6f5f7fc67dcb73b2a8a7a11404703aca210a7c75de9e50d914c9f9942c2").unwrap(),
total_kernel_offset: PrivateKey::from_hex(
"0000000000000000000000000000000000000000000000000000000000000000",
)
.unwrap(),
total_script_offset: PrivateKey::from_hex(
"0000000000000000000000000000000000000000000000000000000000000000",
)
.unwrap(),
nonce: 0,
pow: ProofOfWork {
pow_algo: PowAlgorithm::Sha3x,
pow_data: not_before_proof.clone(),
},
},
body: AggregateBody::new(vec![], vec![], vec![]),
}
}
#[cfg(test)]
mod test {
use jmt::{JellyfishMerkleTree, KeyHash};
use serial_test::serial;
use tari_common_types::types::{CompressedCommitment, UncompressedCommitment};
use tari_mmr::pruned_hashset::PrunedHashSet;
use tari_transaction_components::{
aggregated_body::AggregateBody,
crypto_factories::CryptoFactories,
transaction_components::{KernelFeatures, TransactionOutput, transaction_output::batch_verify_range_proofs},
};
use tari_utilities::ByteArray;
use super::*;
use crate::{
KernelMmr,
MrHashError,
PrunedInputMmr,
PrunedOutputMmr,
block_output_mr_hash_from_pruned_mmr,
chain_storage::{BlockchainBackend, SmtHasher},
consensus::BaseNodeConsensusManager,
input_mr_hash_from_pruned_mmr,
kernel_mr_hash_from_mmr,
test_helpers::blockchain::{TempDatabase, create_new_blockchain_with_network},
validation::{ChainBalanceValidator, FinalHorizonStateValidation},
};
#[test]
#[serial]
fn esmeralda_genesis_sanity_check() {
let network = Network::Esmeralda;
set_network_by_env_var_or_force_set(network);
if !network_matches(network) {
panic!("Network could not be set ('esmeralda_genesis_sanity_check()')");
}
let block = get_esmeralda_genesis_block();
check_block(network, &block, 313, 794, 314);
remove_network_env_var();
}
#[test]
#[serial]
fn nextnet_genesis_sanity_check() {
let network = Network::NextNet;
set_network_by_env_var_or_force_set(network);
if !network_matches(network) {
panic!("Network could not be set ('nextnet_genesis_sanity_check()')");
}
let block = get_nextnet_genesis_block();
check_block(network, &block, 0, 0, 0);
remove_network_env_var();
}
#[test]
#[serial]
fn mainnet_genesis_sanity_check() {
let network = Network::MainNet;
set_network_by_env_var_or_force_set(network);
if !network_matches(network) {
panic!("Network could not be set ('mainnet_genesis_sanity_check()')");
}
let block = get_mainnet_genesis_block();
check_block(network, &block, 253, 674, 254);
remove_network_env_var();
}
#[test]
#[serial]
fn stagenet_genesis_sanity_check() {
let network = Network::StageNet;
set_network_by_env_var_or_force_set(network);
if !network_matches(network) {
panic!("Network could not be set ('stagenet_genesis_sanity_check()')");
}
let block = get_stagenet_genesis_block();
check_block(network, &block, 0, 0, 0);
remove_network_env_var();
}
#[test]
#[serial]
fn igor_genesis_sanity_check() {
let network = Network::Igor;
set_network_by_env_var_or_force_set(network);
if !network_matches(network) {
panic!("Network could not be set ('igor_genesis_sanity_check()')");
}
let block = get_igor_genesis_block();
check_block(network, &block, 0, 0, 0);
remove_network_env_var();
}
#[test]
#[serial]
fn localnet_genesis_sanity_check() {
let network = Network::LocalNet;
set_network_by_env_var_or_force_set(network);
if !network_matches(network) {
panic!("Network could not be set ('localnet_genesis_sanity_check()')");
}
let block = get_localnet_genesis_block();
check_block(network, &block, 0, 0, 0);
remove_network_env_var();
}
#[allow(clippy::too_many_lines)]
fn check_block(
network: Network,
block: &ChainBlock,
expected_inputs: usize,
expected_outputs: usize,
expected_kernels: usize,
) {
assert_eq!(block.block().body.kernels().len(), expected_kernels);
assert_eq!(block.block().body.outputs().len(), expected_outputs);
assert_eq!(block.block().body.inputs().len(), expected_inputs);
let factories = CryptoFactories::default();
let some_output_is_coinbase = block.block().body.outputs().iter().any(|o| o.is_coinbase());
assert!(!some_output_is_coinbase);
let outputs = block.block().body.outputs().iter().collect::<Vec<_>>();
batch_verify_range_proofs(&factories.range_proof, &outputs).unwrap();
assert_eq!(
block.block().body.kernels().len() as u64,
block.header().kernel_mmr_size
);
assert_eq!(
block.block().body.outputs().len() as u64 - block.block().body.inputs().len() as u64,
block.header().output_smt_size
);
for kernel in block.block().body.kernels() {
kernel.verify_signature().unwrap();
}
let some_kernel_contains_coinbase_features = block
.block()
.body
.kernels()
.iter()
.any(|k| k.features.contains(KernelFeatures::COINBASE_KERNEL));
assert!(!some_kernel_contains_coinbase_features);
let mut kernel_mmr = KernelMmr::new(Vec::new());
for k in block.block().body.kernels() {
kernel_mmr.push(k.hash().to_vec()).unwrap();
}
let tempdb = TempDatabase::new();
let tree_reader = tempdb.create_smt_reader().unwrap();
let output_smt = JellyfishMerkleTree::<_, SmtHasher>::new(&tree_reader);
let mut block_output_mmr = PrunedOutputMmr::new(PrunedHashSet::default());
let mut normal_output_mmr = PrunedOutputMmr::new(PrunedHashSet::default());
let mut smt_batch = vec![];
for o in block.block().body.outputs() {
if o.features.is_coinbase() {
block_output_mmr.push(o.hash().to_vec()).unwrap();
} else {
normal_output_mmr.push(o.hash().to_vec()).unwrap();
}
let smt_key = KeyHash(o.commitment.as_bytes().try_into().expect("commitment is 32 bytes"));
let smt_value = o.smt_hash(block.header().height);
smt_batch.push((smt_key, Some(smt_value.to_vec())));
o.verify_metadata_signature().unwrap();
}
block_output_mmr
.push(normal_output_mmr.get_merkle_root().unwrap().to_vec())
.unwrap();
for i in block.block().body.inputs() {
let smt_key = KeyHash(
i.commitment()
.unwrap()
.as_bytes()
.try_into()
.expect("Commitment is 32 bytes"),
);
smt_batch.push((smt_key, None));
}
let mut input_mmr = PrunedInputMmr::new(PrunedHashSet::default());
for input in block.block().body.inputs() {
input_mmr.push(input.canonical_hash().to_vec()).unwrap();
}
assert_eq!(
kernel_mr_hash_from_mmr(&kernel_mmr).unwrap().to_vec().to_hex(),
block.header().kernel_mr.to_vec().to_hex()
);
let (smt_root, _) = output_smt.put_value_set(smt_batch, 0).unwrap();
assert_eq!(smt_root.0.to_vec().to_hex(), block.header().output_mr.to_vec().to_hex(),);
let coinbases = block.block().body.get_coinbase_outputs().into_iter().cloned().collect();
let normal_output_mr = calculate_header_normal_output_mr(&block.block().body).unwrap();
assert_eq!(
calculate_header_block_output_mr(normal_output_mr, &coinbases)
.unwrap()
.to_vec()
.to_hex(),
block.header().block_output_mr.to_vec().to_hex(),
);
assert_eq!(
block_output_mr_hash_from_pruned_mmr(&block_output_mmr)
.unwrap()
.to_vec()
.to_hex(),
block.header().block_output_mr.to_vec().to_hex(),
);
assert_eq!(
input_mr_hash_from_pruned_mmr(&input_mmr).unwrap().to_vec().to_hex(),
block.header().input_mr.to_vec().to_hex(),
);
let input_sum = block
.block()
.body
.inputs()
.iter()
.map(|o| o.commitment().unwrap().to_commitment().unwrap())
.sum::<UncompressedCommitment>();
let output_sum = block
.block()
.body
.outputs()
.iter()
.map(|o| o.commitment.to_commitment().unwrap())
.sum::<UncompressedCommitment>();
let total_utxo_sum = &output_sum - &input_sum;
let kernel_sum = CompressedCommitment::from_commitment(
block
.block()
.body
.kernels()
.iter()
.map(|k| k.excess.to_commitment().unwrap())
.sum(),
);
let db = create_new_blockchain_with_network(network);
let lock = db.db_read_access().unwrap();
ChainBalanceValidator::new(
BaseNodeConsensusManager::builder(network).build().unwrap(),
Default::default(),
)
.validate(
&*lock,
0,
&CompressedCommitment::from_commitment(total_utxo_sum),
&kernel_sum,
&CompressedCommitment::default(),
)
.unwrap();
}
fn set_network_by_env_var_or_force_set(network: Network) {
set_network_by_env_var(network);
if Network::get_current_or_user_setting_or_default() != network {
let _ = Network::set_current(network);
}
}
fn set_network_by_env_var(network: Network) {
if std::env::var("TARI_NETWORK").is_err() {
unsafe { std::env::set_var("TARI_NETWORK", network.as_key_str()) };
}
}
fn remove_network_env_var() {
unsafe { std::env::remove_var("TARI_NETWORK") };
}
fn network_matches(network: Network) -> bool {
let current_network = Network::get_current_or_user_setting_or_default();
if current_network == network {
true
} else {
println!("\nNetwork mismatch!! Required: {network:?}, current: {current_network:?}.\n");
false
}
}
pub fn calculate_header_block_output_mr(
normal_output_mr: FixedHash,
coinbases: &Vec<TransactionOutput>,
) -> Result<FixedHash, MrHashError> {
let mut block_output_mmr = PrunedOutputMmr::new(PrunedHashSet::default());
for o in coinbases {
block_output_mmr.push(o.hash().to_vec())?;
}
block_output_mmr.push(normal_output_mr.to_vec())?;
block_output_mr_hash_from_pruned_mmr(&block_output_mmr)
}
pub fn calculate_header_normal_output_mr(body: &AggregateBody) -> Result<FixedHash, MrHashError> {
let mut normal_output_mmr = PrunedOutputMmr::new(PrunedHashSet::default());
for o in body.outputs() {
if !o.features.is_coinbase() {
normal_output_mmr.push(o.hash().to_vec())?;
}
}
Ok(FixedHash::try_from(normal_output_mmr.get_merkle_root()?)?)
}
}