Skip to content

Commit

Permalink
Mininal merkle proof for monero pow data
Browse files Browse the repository at this point in the history
- Adds a port of Monero's `tree_branch` that returns a merkle proof
- Extensive unit tests for create_merkle_proof
- Uses a merkle proof of the transaction hash set instead of the full
  transaction hash set.
- Adds more extensive unit tests for tree_hash
- tree_hash now has at worst one allocation
- RandomX seed is represented in a fixed size array instead fo a
  unbounded String
- Use monero's consensus de/encoding for MoneroPowData
  • Loading branch information
sdbondi committed Jun 10, 2021
1 parent 330e7b7 commit 4923848
Show file tree
Hide file tree
Showing 24 changed files with 1,422 additions and 518 deletions.
207 changes: 142 additions & 65 deletions Cargo.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use crate::error::MmProxyError;
use chrono::{self, DateTime, Duration, Utc};
use std::{collections::HashMap, sync::Arc};
use tari_app_grpc::tari_rpc::{Block, MinerData};
use tari_core::{crypto::tari_utilities::hex::Hex, proof_of_work::monero_rx::FixedByteArray};
use tokio::sync::RwLock;
use tracing::trace;

Expand Down Expand Up @@ -100,7 +101,7 @@ impl BlockTemplateRepository {

#[derive(Clone, Debug)]
pub struct BlockTemplateData {
pub monero_seed: String,
pub monero_seed: FixedByteArray,
pub tari_block: Block,
pub tari_miner_data: MinerData,
pub monero_difficulty: u64,
Expand Down Expand Up @@ -162,7 +163,7 @@ impl BlockTemplateDataBuilder {
.ok_or_else(|| MmProxyError::MissingDataError("tari_difficulty not provided".to_string()))?;

Ok(BlockTemplateData {
monero_seed,
monero_seed: FixedByteArray::from_hex(&monero_seed).map_err(|_| MmProxyError::InvalidRandomXSeed)?,
tari_block,
tari_miner_data,
monero_difficulty,
Expand Down
22 changes: 15 additions & 7 deletions applications/tari_merge_mining_proxy/src/common/merge_mining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ use std::convert::TryFrom;
use tari_app_grpc::tari_rpc as grpc;
use tari_core::{
blocks::NewBlockTemplate,
proof_of_work::{monero_rx, monero_rx::MoneroData},
proof_of_work::{monero_rx, monero_rx::MoneroPowData},
transactions::transaction::{TransactionKernel, TransactionOutput},
FixedByteArray,
};

pub fn deserialize_monero_block_from_hex<T>(data: T) -> Result<Block, MmProxyError>
Expand All @@ -48,15 +49,22 @@ pub fn serialize_monero_block_to_hex(obj: &Block) -> Result<String, MmProxyError
Ok(bytes)
}

pub fn construct_monero_data(block: Block, seed: String) -> Result<MoneroData, MmProxyError> {
pub fn construct_monero_data(block: Block, seed: FixedByteArray) -> Result<MoneroPowData, MmProxyError> {
let hashes = monero_rx::create_ordered_transaction_hashes_from_block(&block);
let root = monero_rx::tree_hash(&hashes)?;
Ok(MoneroData {
let coinbase_merkle_proof = monero_rx::create_merkle_proof(&hashes, &hashes[0]).ok_or_else(|| {
MmProxyError::MissingDataError(
"create_merkle_proof returned None because the block had no coinbase (which is impossible because the \
Block type does not allow that)"
.to_string(),
)
})?;
Ok(MoneroPowData {
header: block.header,
key: seed,
count: hashes.len() as u16,
transaction_root: root.to_fixed_bytes(),
transaction_hashes: hashes.into_iter().map(|h| h.to_fixed_bytes()).collect(),
randomx_key: seed,
transaction_count: hashes.len() as u16,
merkle_root: root,
coinbase_merkle_proof,
coinbase_tx: block.miner_tx,
})
}
Expand Down
2 changes: 2 additions & 0 deletions applications/tari_merge_mining_proxy/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ pub enum MmProxyError {
CoinbaseBuilderError(#[from] CoinbaseBuildError),
#[error("Unexpected Tari base node response: {0}")]
UnexpectedTariBaseNodeResponse(String),
#[error("Invalid RandomX seed")]
InvalidRandomXSeed,
}

impl From<tonic::Status> for MmProxyError {
Expand Down
4 changes: 2 additions & 2 deletions applications/tari_merge_mining_proxy/src/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ impl InnerService {

let header_mut = block_data.tari_block.header.as_mut().unwrap();
let height = header_mut.height;
header_mut.pow.as_mut().unwrap().pow_data = bincode::serialize(&monero_data)?;
header_mut.pow.as_mut().unwrap().pow_data = monero_rx::serialize(&monero_data);

let mut base_node_client = self.base_node_client.clone();
let start = Instant::now();
Expand Down Expand Up @@ -476,7 +476,7 @@ impl InnerService {

debug!(target: LOG_TARGET, "Creating blockhashing blob from blocktemplate blob",);
// Must be done after the tag is inserted since it will affect the hash of the miner tx
let blockhashing_blob = monero_rx::create_blockhashing_blob(&monero_block)?;
let blockhashing_blob = monero_rx::create_blockhashing_blob_from_block(&monero_block)?;

debug!(target: LOG_TARGET, "blockhashing_blob:{}", blockhashing_blob);
monerod_resp["result"]["blockhashing_blob"] = blockhashing_blob.into();
Expand Down
2 changes: 1 addition & 1 deletion base_layer/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ fs2 = "0.3.0"
hex = "0.4.2"
lmdb-zero = "0.4.4"
log = "0.4"
monero = { version = "^0.9.1", features= ["serde_support"], optional = true }
monero = { version = "^0.13.0", features= ["serde_support"], optional = true }
newtype-ops = "0.1.4"
num = "0.3"
prost = "0.6.1"
Expand Down
2 changes: 1 addition & 1 deletion base_layer/core/src/blocks/genesis_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ pub fn get_weatherwax_genesis_block_raw() -> Block {
prev_hash: vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
],
timestamp: 1_622_550_256.into(),
timestamp: 1_623_151_562.into(), // Tue Jun 08 2021 11:26:02 GMT+0000
output_mr: from_hex("dcc44f39b65e5e1e526887e7d56f7b85e2ea44bd29bc5bc195e6e015d19e1c06").unwrap(),
range_proof_mr: from_hex("e4d7dab49a66358379a901b9a36c10f070aa9d7bdc8ae752947b6fc4e55d255f").unwrap(),
output_mmr_size: 1,
Expand Down
2 changes: 1 addition & 1 deletion base_layer/core/src/chain_storage/blockchain_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ pub trait BlockchainBackend: Send + Sync {
) -> Result<(), ChainStorageError>;

/// This gets the monero seed_height. This will return 0, if the seed is unkown
fn fetch_monero_seed_first_seen_height(&self, seed: &str) -> Result<u64, ChainStorageError>;
fn fetch_monero_seed_first_seen_height(&self, seed: &[u8]) -> Result<u64, ChainStorageError>;

fn fetch_horizon_data(&self) -> Result<Option<HorizonData>, ChainStorageError>;
}
20 changes: 10 additions & 10 deletions base_layer/core/src/chain_storage/blockchain_database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ use crate::{
},
common::rolling_vec::RollingVec,
consensus::{chain_strength_comparer::ChainStrengthComparer, ConsensusConstants, ConsensusManager},
proof_of_work::{monero_rx::MoneroData, PowAlgorithm, TargetDifficultyWindow},
proof_of_work::{monero_rx::MoneroPowData, PowAlgorithm, TargetDifficultyWindow},
tari_utilities::epoch_time::EpochTime,
transactions::{
transaction::{TransactionKernel, TransactionOutput},
Expand Down Expand Up @@ -1067,10 +1067,10 @@ fn insert_block(txn: &mut DbTransaction, block: Arc<ChainBlock>) -> Result<(), C
block_hash.to_hex()
);
if block.header().pow_algo() == PowAlgorithm::Monero {
let monero_seed = MoneroData::from_header(&block.header())
let monero_seed = MoneroPowData::from_header(&block.header())
.map_err(|e| ValidationError::CustomError(e.to_string()))?
.key;
txn.insert_monero_seed_height(&monero_seed, block.height());
.randomx_key;
txn.insert_monero_seed_height(monero_seed.to_vec(), block.height());
}

let height = block.height();
Expand Down Expand Up @@ -1925,31 +1925,31 @@ mod test {
#[test]
fn lmdb_fetch_monero_seeds() {
let db = create_test_blockchain_db();
let seed = "test1".to_string();
let seed = b"test1";
{
let db_read = db.db_read_access().unwrap();
assert_eq!(db_read.fetch_monero_seed_first_seen_height(&seed).unwrap(), 0);
assert_eq!(db_read.fetch_monero_seed_first_seen_height(&seed[..]).unwrap(), 0);
}
{
let mut txn = DbTransaction::new();
txn.insert_monero_seed_height(&seed, 5);
txn.insert_monero_seed_height(seed.to_vec(), 5);
let mut db_write = db.test_db_write_access().unwrap();
assert!(db_write.write(txn).is_ok());
}
{
let db_read = db.db_read_access().unwrap();
assert_eq!(db_read.fetch_monero_seed_first_seen_height(&seed).unwrap(), 5);
assert_eq!(db_read.fetch_monero_seed_first_seen_height(&seed[..]).unwrap(), 5);
}

{
let mut txn = DbTransaction::new();
txn.insert_monero_seed_height(&seed, 2);
txn.insert_monero_seed_height(seed.to_vec(), 2);
let mut db_write = db.db_write_access().unwrap();
assert!(db_write.write(txn).is_ok());
}
{
let db_read = db.db_read_access().unwrap();
assert_eq!(db_read.fetch_monero_seed_first_seen_height(&seed).unwrap(), 2);
assert_eq!(db_read.fetch_monero_seed_first_seen_height(&seed[..]).unwrap(), 2);
}
}

Expand Down
8 changes: 4 additions & 4 deletions base_layer/core/src/chain_storage/db_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,9 +260,9 @@ impl DbTransaction {

/// This will store the seed key with the height. This is called when a block is accepted into the main chain.
/// This will only update the hieght of the seed, if its lower then currently stored.
pub fn insert_monero_seed_height(&mut self, monero_seed: &str, height: u64) {
pub fn insert_monero_seed_height(&mut self, monero_seed: Vec<u8>, height: u64) {
self.operations
.push(WriteOperation::InsertMoneroSeedHeight(monero_seed.to_string(), height));
.push(WriteOperation::InsertMoneroSeedHeight(monero_seed, height));
}
}

Expand Down Expand Up @@ -305,7 +305,7 @@ pub enum WriteOperation {
DeleteBlock(HashOutput),
DeleteOrphanChainTip(HashOutput),
InsertOrphanChainTip(HashOutput),
InsertMoneroSeedHeight(String, u64),
InsertMoneroSeedHeight(Vec<u8>, u64),
UpdatePrunedHashSet {
mmr_tree: MmrTree,
header_hash: HashOutput,
Expand Down Expand Up @@ -395,7 +395,7 @@ impl fmt::Display for WriteOperation {
InsertOrphanChainTip(hash) => write!(f, "InsertOrphanChainTip({})", hash.to_hex()),
DeleteBlock(hash) => write!(f, "DeleteBlock({})", hash.to_hex()),
InsertMoneroSeedHeight(data, height) => {
write!(f, "Insert Monero seed string {} for height: {}", data, height)
write!(f, "Insert Monero seed string {} for height: {}", data.to_hex(), height)
},
InsertChainOrphanBlock(block) => write!(f, "InsertChainOrphanBlock({})", block.hash().to_hex()),
UpdatePrunedHashSet {
Expand Down
10 changes: 5 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 @@ -237,7 +237,7 @@ impl LMDBDatabase {
self.delete_block_body(&write_txn, hash)?;
},
InsertMoneroSeedHeight(data, height) => {
self.insert_monero_seed_height(&write_txn, data, height)?;
self.insert_monero_seed_height(&write_txn, &data, height)?;
},
SetAccumulatedDataForOrphan(chain_header) => {
self.set_accumulated_data_for_orphan(
Expand Down Expand Up @@ -929,12 +929,12 @@ impl LMDBDatabase {
fn insert_monero_seed_height(
&self,
write_txn: &WriteTransaction<'_>,
seed: String,
seed: &[u8],
height: u64,
) -> Result<(), ChainStorageError> {
let current_height = lmdb_get(&write_txn, &self.monero_seed_height_db, seed.as_str())?.unwrap_or(std::u64::MAX);
let current_height = lmdb_get(&write_txn, &self.monero_seed_height_db, seed)?.unwrap_or(std::u64::MAX);
if height < current_height {
lmdb_replace(&write_txn, &self.monero_seed_height_db, seed.as_str(), &height)?;
lmdb_replace(&write_txn, &self.monero_seed_height_db, seed, &height)?;
};
Ok(())
}
Expand Down Expand Up @@ -1844,7 +1844,7 @@ impl BlockchainBackend for LMDBDatabase {
Ok(())
}

fn fetch_monero_seed_first_seen_height(&self, seed: &str) -> Result<u64, ChainStorageError> {
fn fetch_monero_seed_first_seen_height(&self, seed: &[u8]) -> Result<u64, ChainStorageError> {
let txn = self.read_transaction()?;
Ok(lmdb_get(&txn, &self.monero_seed_height_db, seed)?.unwrap_or(0))
}
Expand Down
2 changes: 2 additions & 0 deletions base_layer/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ pub mod consensus;
pub mod iterators;
#[cfg(any(feature = "base_node", feature = "transactions"))]
pub mod proof_of_work;
#[cfg(any(feature = "base_node", feature = "transactions"))]
pub use proof_of_work::monero_rx::FixedByteArray;
#[cfg(feature = "base_node")]
pub mod validation;

Expand Down
4 changes: 2 additions & 2 deletions base_layer/core/src/proof_of_work/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
// 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 crate::proof_of_work::Difficulty;
use crate::proof_of_work::{monero_rx::MergeMineError, Difficulty};
use thiserror::Error;

#[derive(Debug, Error)]
Expand All @@ -33,7 +33,7 @@ pub enum PowError {
InvalidTargetDifficulty { expected: Difficulty, got: Difficulty },
#[cfg(feature = "base_node")]
#[error("Invalid merge mining data or operation: {0}")]
MergeMineError(#[from] super::monero_rx::MergeMineError),
MergeMineError(#[from] MergeMineError),
}

#[derive(Debug, Error, Clone, PartialEq)]
Expand Down
4 changes: 2 additions & 2 deletions base_layer/core/src/proof_of_work/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ mod error;
#[cfg(any(feature = "base_node", feature = "transactions"))]
pub use error::{DifficultyAdjustmentError, PowError};

#[cfg(feature = "base_node")]
#[cfg(any(feature = "base_node", feature = "transactions"))]
pub mod monero_rx;
#[cfg(feature = "base_node")]
#[cfg(any(feature = "base_node", feature = "transactions"))]
pub use monero_rx::monero_difficulty;

#[cfg(any(feature = "base_node", feature = "transactions"))]
Expand Down
42 changes: 42 additions & 0 deletions base_layer/core/src/proof_of_work/monero_rx/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2021, The Tari Project
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
// following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
// disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
// following disclaimer in the documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
// products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// 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 crate::crypto::tari_utilities::hex::HexError;
use randomx_rs::RandomXError;

#[derive(Debug, thiserror::Error)]
pub enum MergeMineError {
#[error("Serialization error: {0}")]
SerializeError(String),
#[error("Error deserializing Monero data: {0}")]
DeserializeError(String),
#[error("Hashing of Monero data failed: {0}")]
HashingError(String),
#[error("RandomX error: {0}")]
RandomXError(#[from] RandomXError),
#[error("Validation error: {0}")]
ValidationError(String),
#[error("Hex conversion error: {0}")]
HexError(#[from] HexError),
#[error("Monero PoW data did not contain a valid merkle root")]
InvalidMerkleRoot,
}
Loading

0 comments on commit 4923848

Please sign in to comment.