Skip to content

Commit

Permalink
feat!: apply hashing api to the mmr (#4445)
Browse files Browse the repository at this point in the history
Description
---
Partial fulfillment for #4395:
- Added hashing API to the MMR by virtue of forcing the hashing type to be `D: Digest + DomainDigest` instead of only `D: Digest` at the lower level MMR methods and functions.
- base_layer/mmr/src/common.rs
- base_layer/mmr/src/merkle_mountain_range.rs
- base_layer/mmr/src/merkle_proof.rs
- base_layer/mmr/src/mutable_mmr.rs

Fixed cucumber function `const getTransactionOutputHash = function (output)`

Motivation and Context
---
Domain separated hashing as per the `tari_crypto` hashing API is needed for the MMR.

How Has This Been Tested?
---
- Passed unit tests
- Passed cucumber tests
  • Loading branch information
hansieodendaal committed Aug 16, 2022
1 parent 4788789 commit d6bab2f
Show file tree
Hide file tree
Showing 24 changed files with 327 additions and 203 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,8 @@ use tari_common_types::types::{Commitment, RangeProofService};
use tari_comms::{connectivity::ConnectivityRequester, peer_manager::NodeId};
use tari_crypto::{
commitment::HomomorphicCommitment,
hash::blake2::Blake256,
tari_utilities::{hex::Hex, Hashable},
};
use tari_mmr::{MerkleMountainRange, MutableMmr};
use tokio::task;

use super::error::HorizonSyncError;
Expand Down Expand Up @@ -75,6 +73,10 @@ use crate::{
TransactionOutput,
},
validation::{helpers, FinalHorizonStateValidation},
MutablePrunedOutputMmr,
PrunedKernelMmr,
PrunedOutputMmr,
PrunedWitnessMmr,
};

const LOG_TARGET: &str = "c::bn::state_machine_service::states::horizon_state_sync";
Expand Down Expand Up @@ -326,7 +328,7 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> {
.fetch_block_accumulated_data(current_header.header().prev_hash.clone())
.await?;
let kernel_pruned_set = block_data.dissolve().0;
let mut kernel_mmr = MerkleMountainRange::<Blake256, _>::new(kernel_pruned_set);
let mut kernel_mmr = PrunedKernelMmr::new(kernel_pruned_set);

for hash in kernel_hashes.drain(..) {
kernel_mmr.push(hash)?;
Expand Down Expand Up @@ -487,8 +489,8 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> {
.await?;
let (_, output_pruned_set, witness_pruned_set, _) = block_data.dissolve();

let mut output_mmr = MerkleMountainRange::<Blake256, _>::new(output_pruned_set);
let mut witness_mmr = MerkleMountainRange::<Blake256, _>::new(witness_pruned_set);
let mut output_mmr = PrunedOutputMmr::new(output_pruned_set);
let mut witness_mmr = PrunedWitnessMmr::new(witness_pruned_set);
let mut constants = self.rules.consensus_constants(current_header.height()).clone();
let mut last_sync_timer = Instant::now();
let mut avg_latency = RollingAverageTime::new(20);
Expand Down Expand Up @@ -596,7 +598,7 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> {
bitmap.run_optimize();

let pruned_output_set = output_mmr.get_pruned_hash_set()?;
let output_mmr = MutableMmr::<Blake256, _>::new(pruned_output_set.clone(), bitmap.clone())?;
let output_mmr = MutablePrunedOutputMmr::new(pruned_output_set.clone(), bitmap.clone())?;

let mmr_root = output_mmr.get_merkle_root()?;
if mmr_root != current_header.header().output_mr {
Expand Down
103 changes: 54 additions & 49 deletions base_layer/core/src/blocks/genesis_block.rs

Large diffs are not rendered by default.

15 changes: 9 additions & 6 deletions base_layer/core/src/chain_storage/blockchain_database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ use tari_common_types::{
chain_metadata::ChainMetadata,
types::{BlockHash, Commitment, HashOutput, Signature},
};
use tari_crypto::hash::blake2::Blake256;
use tari_mmr::{pruned_hashset::PrunedHashSet, MerkleMountainRange, MutableMmr};
use tari_mmr::pruned_hashset::PrunedHashSet;
use tari_utilities::{epoch_time::EpochTime, hex::Hex, ByteArray, Hashable};

use crate::{
Expand Down Expand Up @@ -88,6 +87,10 @@ use crate::{
OrphanValidation,
PostOrphanBodyValidation,
},
MutablePrunedOutputMmr,
PrunedInputMmr,
PrunedKernelMmr,
PrunedWitnessMmr,
};

const LOG_TARGET: &str = "c::cs::database";
Expand Down Expand Up @@ -1215,10 +1218,10 @@ pub fn calculate_mmr_roots<T: BlockchainBackend>(db: &T, block: &Block) -> Resul
value: header.prev_hash.to_hex(),
})?;

let mut kernel_mmr = MerkleMountainRange::<Blake256, _>::new(kernels);
let mut output_mmr = MutableMmr::<Blake256, _>::new(outputs, deleted)?;
let mut witness_mmr = MerkleMountainRange::<Blake256, _>::new(range_proofs);
let mut input_mmr = MerkleMountainRange::<Blake256, _>::new(PrunedHashSet::default());
let mut kernel_mmr = PrunedKernelMmr::new(kernels);
let mut output_mmr = MutablePrunedOutputMmr::new(outputs, deleted)?;
let mut witness_mmr = PrunedWitnessMmr::new(range_proofs);
let mut input_mmr = PrunedInputMmr::new(PrunedHashSet::default());
let mut deleted_outputs = Vec::new();

for kernel in body.kernels().iter() {
Expand Down
12 changes: 7 additions & 5 deletions base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ use tari_common_types::{
chain_metadata::ChainMetadata,
types::{BlockHash, Commitment, HashOutput, Signature, BLOCK_HASH_LENGTH},
};
use tari_crypto::hash::blake2::Blake256;
use tari_mmr::{Hash, MerkleMountainRange, MutableMmr};
use tari_mmr::Hash;
use tari_storage::lmdb_store::{db, LMDBBuilder, LMDBConfig, LMDBStore};
use tari_utilities::{
hash::Hashable,
Expand Down Expand Up @@ -98,6 +97,9 @@ use crate::{
aggregated_body::AggregateBody,
transaction_components::{TransactionError, TransactionInput, TransactionKernel, TransactionOutput},
},
MutablePrunedOutputMmr,
PrunedKernelMmr,
PrunedWitnessMmr,
};

type DatabaseRef = Arc<Database<'static>>;
Expand Down Expand Up @@ -1155,7 +1157,7 @@ impl LMDBDatabase {
..
} = data;

let mut kernel_mmr = MerkleMountainRange::<Blake256, _>::new(pruned_kernel_set);
let mut kernel_mmr = PrunedKernelMmr::new(pruned_kernel_set);

for kernel in kernels {
total_kernel_sum = &total_kernel_sum + &kernel.excess;
Expand All @@ -1170,8 +1172,8 @@ impl LMDBDatabase {
})?;
self.insert_kernel(txn, &block_hash, &kernel, pos)?;
}
let mut output_mmr = MutableMmr::<Blake256, _>::new(pruned_output_set, Bitmap::create())?;
let mut witness_mmr = MerkleMountainRange::<Blake256, _>::new(pruned_proof_set);
let mut output_mmr = MutablePrunedOutputMmr::new(pruned_output_set, Bitmap::create())?;
let mut witness_mmr = PrunedWitnessMmr::new(pruned_proof_set);

let leaf_count = witness_mmr.get_leaf_count()?;

Expand Down
39 changes: 39 additions & 0 deletions base_layer/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,43 @@ pub mod large_ints {
pub struct U512(8);
}
}

pub use large_ints::{U256, U512};
use tari_crypto::{hash::blake2::Blake256, hash_domain, hashing::DomainSeparatedHasher};
use tari_mmr::{pruned_hashset::PrunedHashSet, Hash, MerkleMountainRange, MutableMmr};

hash_domain!(
KernelMmrHashDomain,
"com.tari.tari_project.base_layer.core.kernel_mmr",
1
);
pub type KernelMmrHasherBlake256 = DomainSeparatedHasher<Blake256, KernelMmrHashDomain>;
pub type KernelMmr = MerkleMountainRange<KernelMmrHasherBlake256, Vec<Hash>>;
pub type PrunedKernelMmr = MerkleMountainRange<KernelMmrHasherBlake256, PrunedHashSet>;

hash_domain!(
WitnessMmrHashDomain,
"com.tari.tari_project.base_layer.core.witness_mmr",
1
);
pub type WitnessMmrHasherBlake256 = DomainSeparatedHasher<Blake256, WitnessMmrHashDomain>;
pub type WitnessMmr = MerkleMountainRange<WitnessMmrHasherBlake256, Vec<Hash>>;
pub type PrunedWitnessMmr = MerkleMountainRange<WitnessMmrHasherBlake256, PrunedHashSet>;

hash_domain!(
OutputMmrHashDomain,
"com.tari.tari_project.base_layer.core.output_mmr",
1
);
pub type OutputMmrHasherBlake256 = DomainSeparatedHasher<Blake256, OutputMmrHashDomain>;
pub type MutableOutputMmr = MutableMmr<OutputMmrHasherBlake256, Vec<Hash>>;
pub type PrunedOutputMmr = MerkleMountainRange<OutputMmrHasherBlake256, PrunedHashSet>;
pub type MutablePrunedOutputMmr = MutableMmr<OutputMmrHasherBlake256, PrunedHashSet>;

hash_domain!(
InputMmrHashDomain,
"com.tari.tari_project.base_layer.core.output_mmr",
1
);
pub type InputMmrHasherBlake256 = DomainSeparatedHasher<Blake256, InputMmrHashDomain>;
pub type PrunedInputMmr = MerkleMountainRange<InputMmrHasherBlake256, PrunedHashSet>;
23 changes: 15 additions & 8 deletions base_layer/core/tests/helpers/block_builders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,17 @@ use tari_core::{
},
CryptoFactories,
},
KernelMmr,
KernelMmrHasherBlake256,
MutableOutputMmr,
WitnessMmr,
WitnessMmrHasherBlake256,
};
use tari_crypto::{
hash::blake2::Blake256,
keys::PublicKey as PublicKeyTrait,
tari_utilities::{hash::Hashable, hex::Hex},
};
use tari_mmr::{MerkleMountainRange, MutableMmr};
use tari_mmr::{Hash, MutableMmr};
use tari_script::script;

pub fn create_coinbase(
Expand Down Expand Up @@ -151,12 +155,12 @@ fn print_new_genesis_block(network: Network) {
.build()
.unwrap();

let mut kernel_mmr = MerkleMountainRange::<Blake256, _>::new(Vec::new());
let mut kernel_mmr = KernelMmr::new(Vec::new());
kernel_mmr.push(kernel.hash()).unwrap();

let mut witness_mmr = MerkleMountainRange::<Blake256, _>::new(Vec::new());
let mut witness_mmr = WitnessMmr::new(Vec::new());
witness_mmr.push(utxo.witness_hash()).unwrap();
let mut output_mmr = MutableMmr::<Blake256, _>::new(Vec::new(), Bitmap::create()).unwrap();
let mut output_mmr = MutableOutputMmr::new(Vec::new(), Bitmap::create()).unwrap();
output_mmr.push(utxo.hash()).unwrap();

header.kernel_mr = kernel_mmr.get_merkle_root().unwrap();
Expand Down Expand Up @@ -235,6 +239,9 @@ pub fn create_genesis_block(

// Calculate the MMR Merkle roots for the genesis block template and update the header.
fn update_genesis_block_mmr_roots(template: NewBlockTemplate) -> Result<Block, ChainStorageError> {
type BaseLayerKernelMutableMmr = MutableMmr<KernelMmrHasherBlake256, Vec<Hash>>;
type BaseLayerWitnessMutableMmr = MutableMmr<WitnessMmrHasherBlake256, Vec<Hash>>;

let NewBlockTemplate { header, mut body, .. } = template;
// Make sure the body components are sorted. If they already are, this is a very cheap call.
body.sort();
Expand All @@ -243,13 +250,13 @@ fn update_genesis_block_mmr_roots(template: NewBlockTemplate) -> Result<Block, C
let rp_hashes: Vec<HashOutput> = body.outputs().iter().map(|out| out.witness_hash()).collect();

let mut header = BlockHeader::from(header);
header.kernel_mr = MutableMmr::<Blake256, _>::new(kernel_hashes, Bitmap::create())
header.kernel_mr = BaseLayerKernelMutableMmr::new(kernel_hashes, Bitmap::create())
.unwrap()
.get_merkle_root()?;
header.output_mr = MutableMmr::<Blake256, _>::new(out_hashes, Bitmap::create())
header.output_mr = MutableOutputMmr::new(out_hashes, Bitmap::create())
.unwrap()
.get_merkle_root()?;
header.witness_mr = MutableMmr::<Blake256, _>::new(rp_hashes, Bitmap::create())
header.witness_mr = BaseLayerWitnessMutableMmr::new(rp_hashes, Bitmap::create())
.unwrap()
.get_merkle_root()?;
Ok(Block { header, body })
Expand Down
13 changes: 11 additions & 2 deletions base_layer/mmr/benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,16 @@ mod benches {
use blake2::Blake2b;
use criterion::{criterion_group, BatchSize, Criterion};
use digest::Digest;
use tari_mmr::MerkleMountainRange;
use tari_crypto::{hash::blake2::Blake256, hash_domain, hashing::DomainSeparatedHasher};
use tari_mmr::{Hash, MerkleMountainRange};

hash_domain!(
MmrBenchTestHashDomain,
"com.tari.tari_project.base_layer.mmr.bemches",
1
);
pub type MmrTestHasherBlake256 = DomainSeparatedHasher<Blake256, MmrBenchTestHashDomain>;
pub type TestMmr = MerkleMountainRange<MmrTestHasherBlake256, Vec<Hash>>;

fn get_hashes(n: usize) -> Vec<Vec<u8>> {
(0..n).map(|i| Blake2b::digest(&i.to_le_bytes()).to_vec()).collect()
Expand All @@ -43,7 +52,7 @@ mod benches {
fn build_mmr(c: &mut Criterion) {
c.bench_function("Build MMR", move |b| {
let hashes = get_hashes(1000);
let mut mmr = MerkleMountainRange::<Blake2b, _>::new(Vec::default());
let mut mmr = TestMmr::new(Vec::default());
b.iter_batched(
|| hashes.clone(),
|hashes| {
Expand Down
3 changes: 2 additions & 1 deletion base_layer/mmr/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use std::convert::TryInto;

use digest::Digest;
use tari_common::DomainDigest;

use crate::{error::MerkleMountainRangeError, Hash};

Expand Down Expand Up @@ -171,7 +172,7 @@ pub fn is_left_sibling(pos: usize) -> bool {
(peak_map & peak) == 0
}

pub fn hash_together<D: Digest>(left: &[u8], right: &[u8]) -> Hash {
pub fn hash_together<D: Digest + DomainDigest>(left: &[u8], right: &[u8]) -> Hash {
D::new().chain(left).chain(right).finalize().to_vec()
}

Expand Down
9 changes: 5 additions & 4 deletions base_layer/mmr/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use crate::{
};
use digest::Digest;
use std::{convert::TryFrom, marker::PhantomData};
use tari_common::DomainDigest;

pub type PrunedMmr<D> = MerkleMountainRange<D, PrunedHashSet>;
pub type PrunedMutableMmr<D> = MutableMmr<D, PrunedHashSet>;
Expand All @@ -41,7 +42,7 @@ pub type PrunedMutableMmr<D> = MutableMmr<D, PrunedHashSet>;
/// `validate` will throw an error.
pub fn prune_mmr<D, B>(mmr: &MerkleMountainRange<D, B>) -> Result<PrunedMmr<D>, MerkleMountainRangeError>
where
D: Digest,
D: Digest + DomainDigest,
B: ArrayLike<Value = Hash>,
{
let backend = PrunedHashSet::try_from(mmr)?;
Expand All @@ -54,7 +55,7 @@ where
/// A convenience function in the same vein as [prune_mmr], but applied to `MutableMmr` instances.
pub fn prune_mutable_mmr<D, B>(mmr: &MutableMmr<D, B>) -> Result<PrunedMutableMmr<D>, MerkleMountainRangeError>
where
D: Digest,
D: Digest + DomainDigest,
B: ArrayLike<Value = Hash>,
{
let backend = PrunedHashSet::try_from(&mmr.mmr)?;
Expand Down Expand Up @@ -84,7 +85,7 @@ pub fn calculate_pruned_mmr_root<D, B>(
deletions: Vec<u32>,
) -> Result<Hash, MerkleMountainRangeError>
where
D: Digest,
D: Digest + DomainDigest,
B: ArrayLike<Value = Hash>,
{
let mut pruned_mmr = prune_mutable_mmr(src)?;
Expand All @@ -103,7 +104,7 @@ pub fn calculate_mmr_root<D, B>(
additions: Vec<Hash>,
) -> Result<Hash, MerkleMountainRangeError>
where
D: Digest,
D: Digest + DomainDigest,
B: ArrayLike<Value = Hash>,
{
let mut mmr = prune_mmr(src)?;
Expand Down
3 changes: 2 additions & 1 deletion base_layer/mmr/src/merkle_checkpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use serde::{
de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor},
ser::{Serialize, SerializeStruct, Serializer},
};
use tari_common::DomainDigest;

use crate::{backend::ArrayLike, error::MerkleMountainRangeError, mutable_mmr::MutableMmr, Hash};

Expand Down Expand Up @@ -55,7 +56,7 @@ impl MerkleCheckPoint {
/// from here.
pub fn apply<D, B2>(&self, mmr: &mut MutableMmr<D, B2>) -> Result<(), MerkleMountainRangeError>
where
D: Digest,
D: Digest + DomainDigest,
B2: ArrayLike<Value = Hash>,
{
for node in &self.nodes_added {
Expand Down
5 changes: 3 additions & 2 deletions base_layer/mmr/src/merkle_mountain_range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use std::{
};

use digest::Digest;
use tari_common::DomainDigest;

use crate::{
backend::ArrayLike,
Expand Down Expand Up @@ -57,7 +58,7 @@ pub struct MerkleMountainRange<D, B> {

impl<D, B> MerkleMountainRange<D, B>
where
D: Digest,
D: Digest + DomainDigest,
B: ArrayLike<Value = Hash>,
{
/// Create a new Merkle mountain range using the given backend for storage
Expand Down Expand Up @@ -276,7 +277,7 @@ where

impl<D, B, B2> PartialEq<MerkleMountainRange<D, B2>> for MerkleMountainRange<D, B>
where
D: Digest,
D: Digest + DomainDigest,
B: ArrayLike<Value = Hash>,
B2: ArrayLike<Value = Hash>,
{
Expand Down
Loading

0 comments on commit d6bab2f

Please sign in to comment.