From ee0b139e4512c1fd430284bf4e6919525009a22f Mon Sep 17 00:00:00 2001 From: skoupidi Date: Wed, 7 May 2025 10:32:39 +0300 Subject: [PATCH] script/research/powdata: moved into blockchain/monero --- Cargo.lock | 2 + Cargo.toml | 4 + script/research/powdata/.gitignore | 2 - script/research/powdata/Cargo.toml | 15 -- script/research/powdata/src/error.rs | 5 - src/blockchain/mod.rs | 3 + src/blockchain/monero/keccak.rs | 107 +++++++++++ src/blockchain/monero/merkle_proof.rs | 156 ++++++++++++++++ .../lib.rs => src/blockchain/monero/mod.rs | 136 ++++---------- .../blockchain/monero/utils.rs | 176 +++--------------- src/error.rs | 3 + 11 files changed, 344 insertions(+), 265 deletions(-) delete mode 100644 script/research/powdata/.gitignore delete mode 100644 script/research/powdata/Cargo.toml delete mode 100644 script/research/powdata/src/error.rs create mode 100644 src/blockchain/monero/keccak.rs create mode 100644 src/blockchain/monero/merkle_proof.rs rename script/research/powdata/src/lib.rs => src/blockchain/monero/mod.rs (57%) rename script/research/powdata/src/merkle_tree.rs => src/blockchain/monero/utils.rs (54%) diff --git a/Cargo.lock b/Cargo.lock index e14cfa115..ac824341e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2070,6 +2070,7 @@ dependencies = [ "lazy_static", "libc", "log", + "monero", "num-bigint", "pin-project-lite 0.2.16", "plotters", @@ -2088,6 +2089,7 @@ dependencies = [ "structopt", "structopt-toml", "thiserror 2.0.12", + "tiny-keccak", "tinyjson", "toml 0.8.22", "tor-cell", diff --git a/Cargo.toml b/Cargo.toml index cbcabe1f5..004a4465f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -127,6 +127,8 @@ sled-overlay = {version = "0.1.7", optional = true} # Miner randomx = {git = "https://codeberg.org/darkrenaissance/RandomX", optional = true} +monero = {version = "0.21.0", optional = true} +tiny-keccak = { version = "2.0.2", features = ["keccak"], optional = true } [dev-dependencies] clap = {version = "4.4.11", features = ["derive"]} @@ -152,7 +154,9 @@ async-sdk = [ blockchain = [ "sled-overlay/serial", + "monero", "num-bigint", + "tiny-keccak", "darkfi-serial/num-bigint", diff --git a/script/research/powdata/.gitignore b/script/research/powdata/.gitignore deleted file mode 100644 index 2c96eb1b6..000000000 --- a/script/research/powdata/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -target/ -Cargo.lock diff --git a/script/research/powdata/Cargo.toml b/script/research/powdata/Cargo.toml deleted file mode 100644 index f9727fde5..000000000 --- a/script/research/powdata/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "xmrpowdata" -version = "0.4.1" -authors = ["Dyne.org foundation "] -license = "AGPL-3.0-only" -edition = "2021" - -[workspace] - -[dependencies] -monero = "0.21.0" -tiny-keccak = { version = "2.0.2", features = ["keccak"] } -thiserror = "2.0.12" - -darkfi-serial = { path = "../../../src/serial" } diff --git a/script/research/powdata/src/error.rs b/script/research/powdata/src/error.rs deleted file mode 100644 index 35c929d67..000000000 --- a/script/research/powdata/src/error.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[derive(Debug, thiserror::Error)] -pub enum MergeMineError { - #[error("Hashing of Monero data failed: {0}")] - HashingError(String), -} diff --git a/src/blockchain/mod.rs b/src/blockchain/mod.rs index 75ea3467c..fad4fcf7b 100644 --- a/src/blockchain/mod.rs +++ b/src/blockchain/mod.rs @@ -50,6 +50,9 @@ pub use contract_store::{ ContractStore, ContractStoreOverlay, SLED_BINCODE_TREE, SLED_CONTRACTS_TREE, }; +/// Monero definitions needed for merge mining +pub mod monero; + /// Structure holding all sled trees that define the concept of Blockchain. #[derive(Clone)] pub struct Blockchain { diff --git a/src/blockchain/monero/keccak.rs b/src/blockchain/monero/keccak.rs new file mode 100644 index 000000000..1d21ba5ea --- /dev/null +++ b/src/blockchain/monero/keccak.rs @@ -0,0 +1,107 @@ +/* This file is part of DarkFi (https://dark.fi) + * + * Copyright (C) 2020-2025 Dyne.org foundation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +use std::io::{Cursor, Read, Result, Write}; + +#[allow(unused_imports)] +use tiny_keccak::{Hasher, Keccak}; + +#[repr(C)] +#[allow(unused)] +enum Mode { + Absorbing, + Squeezing, +} + +#[repr(C)] +// https://docs.rs/tiny-keccak/latest/src/tiny_keccak/lib.rs.html#368 +struct KeccakState { + buffer: [u8; 200], + offset: usize, + rate: usize, + delim: u8, + mode: Mode, +} + +unsafe fn serialize_keccak(keccak: &Keccak, writer: &mut W) -> Result<()> { + let keccak_ptr = keccak as *const Keccak as *const KeccakState; + let keccak_state = &*keccak_ptr; + + writer.write_all(&keccak_state.buffer)?; + writer.write_all(&(keccak_state.offset as u64).to_le_bytes())?; + writer.write_all(&(keccak_state.rate as u64).to_le_bytes())?; + writer.write_all(&[keccak_state.delim])?; + + Ok(()) +} + +unsafe fn deserialize_keccak(reader: &mut R) -> Result { + let mut keccak = Keccak::v256(); + + let keccak_ptr = &mut keccak as *mut Keccak as *mut KeccakState; + let keccak_state = &mut *keccak_ptr; + + reader.read_exact(&mut keccak_state.buffer)?; + + let mut offset_bytes = [0u8; 8]; + reader.read_exact(&mut offset_bytes)?; + keccak_state.offset = u64::from_le_bytes(offset_bytes) as usize; + + let mut rate_bytes = [0u8; 8]; + reader.read_exact(&mut rate_bytes)?; + keccak_state.rate = u64::from_le_bytes(rate_bytes) as usize; + + let mut delim_byte = [0u8; 1]; + reader.read_exact(&mut delim_byte)?; + keccak_state.delim = delim_byte[0]; + + keccak_state.mode = Mode::Absorbing; + + Ok(keccak) +} + +pub fn keccak_to_bytes(keccak: &Keccak) -> Vec { + let mut bytes = vec![]; + unsafe { serialize_keccak(keccak, &mut bytes).unwrap() } + bytes +} + +pub fn keccak_from_bytes(bytes: &[u8]) -> Keccak { + let mut cursor = Cursor::new(bytes); + unsafe { deserialize_keccak(&mut cursor).unwrap() } +} + +#[test] +fn test_keccak_serde() { + let mut keccak = Keccak::v256(); + keccak.update(b"foobar"); + + let ser = keccak_to_bytes(&keccak); + + let mut digest1 = [0u8; 32]; + keccak.finalize(&mut digest1); + + let de = keccak_from_bytes(&ser); + let mut digest2 = [0u8; 32]; + de.finalize(&mut digest2); + + println!("{:?}", digest1); + println!("{:?}", digest2); + + assert_eq!(digest1, digest2); +} diff --git a/src/blockchain/monero/merkle_proof.rs b/src/blockchain/monero/merkle_proof.rs new file mode 100644 index 000000000..8dbedd6da --- /dev/null +++ b/src/blockchain/monero/merkle_proof.rs @@ -0,0 +1,156 @@ +/* This file is part of DarkFi (https://dark.fi) + * + * Copyright (C) 2020-2025 Dyne.org foundation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +use std::io::{Error, Read, Result, Write}; + +use darkfi_serial::{Decodable, Encodable, ReadExt}; +use monero::{ + consensus::{Decodable as XmrDecodable, Encodable as XmrEncodable}, + Hash, +}; + +use super::utils::cn_fast_hash2; + +const MAX_MERKLE_TREE_PROOF_SIZE: usize = 32; + +/// The Monero Merkle proof +#[derive(Debug, Clone)] +pub struct MerkleProof { + branch: Vec, + path_bitmap: u32, +} + +impl Encodable for MerkleProof { + fn encode(&self, s: &mut S) -> Result { + let mut n = 0; + + let len = self.branch.len() as u8; + n += len.encode(s)?; + + for hash in &self.branch { + n += (*hash).consensus_encode(s)?; + } + + n += self.path_bitmap.encode(s)?; + + Ok(n) + } +} + +impl Decodable for MerkleProof { + fn decode(d: &mut D) -> Result { + let len: u8 = d.read_u8()?; + let mut branch = Vec::with_capacity(len as usize); + + for _ in 0..len { + branch.push(Hash::consensus_decode(d).map_err(|_| Error::other("Invalid XMR hash"))?); + } + + let path_bitmap: u32 = Decodable::decode(d)?; + + Ok(Self { branch, path_bitmap }) + } +} + +impl MerkleProof { + pub fn try_construct(branch: Vec, path_bitmap: u32) -> Option { + if branch.len() >= MAX_MERKLE_TREE_PROOF_SIZE { + return None + } + + Some(Self { branch, path_bitmap }) + } + + /// Returns the Merkle proof branch as a list of Monero hashes + #[inline] + pub fn branch(&self) -> &[Hash] { + &self.branch + } + + /// Returns the path bitmap of the proof + pub fn path(&self) -> u32 { + self.path_bitmap + } + + /// The coinbase must be the first transaction in the block, so + /// that you can't have multiple coinbases in a block. That means + /// the coinbase is always the leftmost branch in the Merkle tree. + /// This tests that the given proof is for the leftmost branch in + /// the Merkle tree. + pub fn check_coinbase_path(&self) -> bool { + if self.path_bitmap == 0b00000000 { + return true; + } + false + } + + /// Calculates the Merkle root hash from the provided Monero hash + pub fn calculate_root_with_pos(&self, hash: &Hash, aux_chain_count: u8) -> (Hash, u32) { + let root = self.calculate_root(hash); + let pos = self.get_position_from_path(u32::from(aux_chain_count)); + (root, pos) + } + + pub fn calculate_root(&self, hash: &Hash) -> Hash { + if self.branch.is_empty() { + return *hash; + } + + let mut root = *hash; + let depth = self.branch.len(); + for d in 0..depth { + if (self.path_bitmap >> (depth - d - 1)) & 1 > 0 { + root = cn_fast_hash2(&self.branch[d], &root); + } else { + root = cn_fast_hash2(&root, &self.branch[d]); + } + } + + root + } + + pub fn get_position_from_path(&self, aux_chain_count: u32) -> u32 { + if aux_chain_count <= 1 { + return 0 + } + + let mut depth = 0; + let mut k = 1; + + while k < aux_chain_count { + depth += 1; + k <<= 1; + } + + k -= aux_chain_count; + + let mut pos = 0; + let mut path = self.path_bitmap; + + for _i in 1..depth { + pos = (pos << 1) | (path & 1); + path >>= 1; + } + + if pos < k { + return pos + } + + (((pos - k) << 1) | (path & 1)) + k + } +} diff --git a/script/research/powdata/src/lib.rs b/src/blockchain/monero/mod.rs similarity index 57% rename from script/research/powdata/src/lib.rs rename to src/blockchain/monero/mod.rs index 04b840b44..ad878a821 100644 --- a/script/research/powdata/src/lib.rs +++ b/src/blockchain/monero/mod.rs @@ -1,4 +1,22 @@ -use std::io::{self, Read, Write}; +/* This file is part of DarkFi (https://dark.fi) + * + * Copyright (C) 2020-2025 Dyne.org foundation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +use std::io::{Error, Read, Result, Write}; use darkfi_serial::{Decodable, Encodable}; use monero::{ @@ -6,18 +24,24 @@ use monero::{ consensus::{Decodable as XmrDecodable, Encodable as XmrEncodable}, cryptonote::hash::Hashable, util::ringct::{RctSigBase, RctType}, + BlockHeader, Hash, }; use tiny_keccak::{Hasher, Keccak}; -mod error; +mod merkle_proof; +use merkle_proof::MerkleProof; -mod merkle_tree; -use merkle_tree::MerkleProof; +mod keccak; +use keccak::{keccak_from_bytes, keccak_to_bytes}; +mod utils; + +/// This struct represents all the Proof of Work information required +/// for merge mining. #[derive(Clone)] pub struct MoneroPowData { /// Monero Header fields - pub header: monero::BlockHeader, + pub header: BlockHeader, /// RandomX VM key - length varies to a max len of 60. /// TODO: Implement a type, or use randomx_key[0] to define len. pub randomx_key: [u8; 64], @@ -25,7 +49,7 @@ pub struct MoneroPowData { /// This is used to produce the blockhashing_blob. pub transaction_count: u16, /// Transaction root - pub merkle_root: monero::Hash, + pub merkle_root: Hash, /// Coinbase Merkle proof hashes pub coinbase_merkle_proof: MerkleProof, /// Incomplete hashed state of the coinbase transaction @@ -37,7 +61,7 @@ pub struct MoneroPowData { } impl Encodable for MoneroPowData { - fn encode(&self, s: &mut S) -> io::Result { + fn encode(&self, s: &mut S) -> Result { let mut n = 0; n += self.header.consensus_encode(s)?; @@ -58,15 +82,15 @@ impl Encodable for MoneroPowData { } impl Decodable for MoneroPowData { - fn decode(d: &mut D) -> io::Result { - let header = monero::BlockHeader::consensus_decode(d) - .map_err(|_| io::Error::other("Invalid XMR header"))?; + fn decode(d: &mut D) -> Result { + let header = + BlockHeader::consensus_decode(d).map_err(|_| Error::other("Invalid XMR header"))?; let randomx_key: [u8; 64] = Decodable::decode(d)?; let transaction_count: u16 = Decodable::decode(d)?; let merkle_root: [u8; 32] = Decodable::decode(d)?; - let merkle_root = monero::Hash::from_slice(&merkle_root); + let merkle_root = Hash::from_slice(&merkle_root); let coinbase_merkle_proof: MerkleProof = Decodable::decode(d)?; @@ -101,7 +125,7 @@ impl MoneroPowData { let mut prefix_hash: [u8; 32] = [0; 32]; finalised_prefix_keccak.finalize(&mut prefix_hash); - let final_prefix_hash = monero::Hash::from_slice(&prefix_hash); + let final_prefix_hash = Hash::from_slice(&prefix_hash); // let mut finalised_keccak = Keccak::v256(); let rct_sig_base = RctSigBase { @@ -112,96 +136,12 @@ impl MoneroPowData { out_pk: vec![], }; - let hashes = vec![final_prefix_hash, rct_sig_base.hash(), monero::Hash::null()]; + let hashes = vec![final_prefix_hash, rct_sig_base.hash(), Hash::null()]; let encoder_final: Vec = hashes.into_iter().flat_map(|h| Vec::from(&h.to_bytes()[..])).collect(); - let coinbase_hash = monero::Hash::new(encoder_final); + let coinbase_hash = Hash::new(encoder_final); let merkle_root = self.coinbase_merkle_proof.calculate_root(&coinbase_hash); (self.merkle_root == merkle_root) && self.coinbase_merkle_proof.check_coinbase_path() } } - -#[repr(C)] -enum Mode { - Absorbing, - Squeezing, -} - -#[repr(C)] -// https://docs.rs/tiny-keccak/latest/src/tiny_keccak/lib.rs.html#368 -struct KeccakState { - buffer: [u8; 200], - offset: usize, - rate: usize, - delim: u8, - mode: Mode, -} - -unsafe fn serialize_keccak(keccak: &Keccak, writer: &mut W) -> io::Result<()> { - let keccak_ptr = keccak as *const Keccak as *const KeccakState; - let keccak_state = &*keccak_ptr; - - writer.write_all(&keccak_state.buffer)?; - writer.write_all(&(keccak_state.offset as u64).to_le_bytes())?; - writer.write_all(&(keccak_state.rate as u64).to_le_bytes())?; - writer.write_all(&[keccak_state.delim])?; - - Ok(()) -} - -unsafe fn deserialize_keccak(reader: &mut R) -> io::Result { - let mut keccak = Keccak::v256(); - - let keccak_ptr = &mut keccak as *mut Keccak as *mut KeccakState; - let keccak_state = &mut *keccak_ptr; - - reader.read_exact(&mut keccak_state.buffer)?; - - let mut offset_bytes = [0u8; 8]; - reader.read_exact(&mut offset_bytes)?; - keccak_state.offset = u64::from_le_bytes(offset_bytes) as usize; - - let mut rate_bytes = [0u8; 8]; - reader.read_exact(&mut rate_bytes)?; - keccak_state.rate = u64::from_le_bytes(rate_bytes) as usize; - - let mut delim_byte = [0u8; 1]; - reader.read_exact(&mut delim_byte)?; - keccak_state.delim = delim_byte[0]; - - keccak_state.mode = Mode::Absorbing; - - Ok(keccak) -} - -fn keccak_to_bytes(keccak: &Keccak) -> Vec { - let mut bytes = vec![]; - unsafe { serialize_keccak(keccak, &mut bytes).unwrap() } - bytes -} - -fn keccak_from_bytes(bytes: &[u8]) -> Keccak { - let mut cursor = io::Cursor::new(bytes); - unsafe { deserialize_keccak(&mut cursor).unwrap() } -} - -#[test] -fn test_keccak_serde() { - let mut keccak = Keccak::v256(); - keccak.update(b"foobar"); - - let ser = keccak_to_bytes(&keccak); - - let mut digest1 = [0u8; 32]; - keccak.finalize(&mut digest1); - - let de = keccak_from_bytes(&ser); - let mut digest2 = [0u8; 32]; - de.finalize(&mut digest2); - - println!("{:?}", digest1); - println!("{:?}", digest2); - - assert_eq!(digest1, digest2); -} diff --git a/script/research/powdata/src/merkle_tree.rs b/src/blockchain/monero/utils.rs similarity index 54% rename from script/research/powdata/src/merkle_tree.rs rename to src/blockchain/monero/utils.rs index 1dacd2d9d..3206dd1a0 100644 --- a/script/research/powdata/src/merkle_tree.rs +++ b/src/blockchain/monero/utils.rs @@ -1,22 +1,33 @@ -use std::io::{self, Read, Write}; +/* This file is part of DarkFi (https://dark.fi) + * + * Copyright (C) 2020-2025 Dyne.org foundation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ -use darkfi_serial::{Decodable, Encodable, ReadExt}; -use monero::{ - consensus::{Decodable as XmrDecodable, Encodable as XmrEncodable}, - Hash, -}; +use monero::Hash; -use crate::error::MergeMineError; - -const MAX_MERKLE_TREE_PROOF_SIZE: usize = 32; +use super::MerkleProof; +use crate::{Error, Result}; /// Returns the Keccak 256 hash of the byte input -fn cn_fast_hash(data: &[u8]) -> Hash { +pub fn cn_fast_hash(data: &[u8]) -> Hash { Hash::new(data) } /// Returns the Keccak 256 hash of 2 hashes -fn cn_fast_hash2(hash1: &Hash, hash2: &Hash) -> Hash { +pub fn cn_fast_hash2(hash1: &Hash, hash2: &Hash) -> Hash { let mut tmp = [0u8; 64]; tmp[..32].copy_from_slice(hash1.as_bytes()); tmp[32..].copy_from_slice(hash2.as_bytes()); @@ -26,16 +37,17 @@ fn cn_fast_hash2(hash1: &Hash, hash2: &Hash) -> Hash { /// Round down to power of two. /// Should error for count < 3 or if the count is unreasonably large /// for tree hash calculations. -fn tree_hash_count(count: usize) -> Result { +#[allow(unused)] +fn tree_hash_count(count: usize) -> Result { if count < 3 { - return Err(MergeMineError::HashingError(format!( + return Err(Error::MoneroHashingError(format!( "Cannot calculate tree hash root. Expected count to be >3 but got {}", count ))); } if count > 0x10000000 { - return Err(MergeMineError::HashingError(format!( + return Err(Error::MoneroHashingError(format!( "Cannot calculate tree hash root. Expected count to be less than 0x10000000 but got {}", count ))); @@ -51,9 +63,10 @@ fn tree_hash_count(count: usize) -> Result { } /// Tree hash algorithm in Monero -pub fn tree_hash(hashes: &[Hash]) -> Result { +#[allow(unused)] +pub fn tree_hash(hashes: &[Hash]) -> Result { if hashes.is_empty() { - return Err(MergeMineError::HashingError( + return Err(Error::MoneroHashingError( "Cannot calculate Merkle root, no hashes".to_string(), )); } @@ -79,7 +92,7 @@ pub fn tree_hash(hashes: &[Hash]) -> Result { } if i != hashes.len() { - return Err(MergeMineError::HashingError( + return Err(Error::MoneroHashingError( "Cannot calculate the Merkle root, hashes not equal to count".to_string(), )); } @@ -98,138 +111,11 @@ pub fn tree_hash(hashes: &[Hash]) -> Result { } } -/// The Monero Merkle proof -#[derive(Debug, Clone)] -pub struct MerkleProof { - branch: Vec, - path_bitmap: u32, -} - -impl Encodable for MerkleProof { - fn encode(&self, s: &mut S) -> io::Result { - let mut n = 0; - - let len = self.branch.len() as u8; - n += len.encode(s)?; - - for hash in &self.branch { - n += (*hash).consensus_encode(s)?; - } - - n += self.path_bitmap.encode(s)?; - - Ok(n) - } -} - -impl Decodable for MerkleProof { - fn decode(d: &mut D) -> io::Result { - let len: u8 = d.read_u8()?; - let mut branch = Vec::with_capacity(len as usize); - - for _ in 0..len { - branch - .push(Hash::consensus_decode(d).map_err(|_| io::Error::other("Invalid XMR hash"))?); - } - - let path_bitmap: u32 = Decodable::decode(d)?; - - Ok(Self { branch, path_bitmap }) - } -} - -impl MerkleProof { - fn try_construct(branch: Vec, path_bitmap: u32) -> Option { - if branch.len() >= MAX_MERKLE_TREE_PROOF_SIZE { - return None - } - - Some(Self { branch, path_bitmap }) - } - - /// Returns the Merkle proof branch as a list of Monero hashes - #[inline] - pub fn branch(&self) -> &[Hash] { - &self.branch - } - - /// Returns the path bitmap of the proof - pub fn path(&self) -> u32 { - self.path_bitmap - } - - /// The coinbase must be the first transaction in the block, so - /// that you can't have multiple coinbases in a block. That means - /// the coinbase is always the leftmost branch in the Merkle tree. - /// This tests that the given proof is for the leftmost branch in - /// the Merkle tree. - pub fn check_coinbase_path(&self) -> bool { - if self.path_bitmap == 0b00000000 { - return true; - } - false - } - - /// Calculates the Merkle root hash from the provided Monero hash - pub fn calculate_root_with_pos(&self, hash: &Hash, aux_chain_count: u8) -> (Hash, u32) { - let root = self.calculate_root(hash); - let pos = self.get_position_from_path(u32::from(aux_chain_count)); - (root, pos) - } - - pub fn calculate_root(&self, hash: &Hash) -> Hash { - if self.branch.is_empty() { - return *hash; - } - - let mut root = *hash; - let depth = self.branch.len(); - for d in 0..depth { - if (self.path_bitmap >> (depth - d - 1)) & 1 > 0 { - root = cn_fast_hash2(&self.branch[d], &root); - } else { - root = cn_fast_hash2(&root, &self.branch[d]); - } - } - - root - } - - pub fn get_position_from_path(&self, aux_chain_count: u32) -> u32 { - if aux_chain_count <= 1 { - return 0 - } - - let mut depth = 0; - let mut k = 1; - - while k < aux_chain_count { - depth += 1; - k <<= 1; - } - - k -= aux_chain_count; - - let mut pos = 0; - let mut path = self.path_bitmap; - - for _i in 1..depth { - pos = (pos << 1) | (path & 1); - path >>= 1; - } - - if pos < k { - return pos - } - - (((pos - k) << 1) | (path & 1)) + k - } -} - /// Creates a Merkle proof for the given hash within the set of hashes. /// This function returns None if the hash is not in hashes. /// This is a port of Monero's tree_branch function. #[allow(clippy::cognitive_complexity)] +#[allow(unused)] pub fn create_merkle_proof(hashes: &[Hash], hash: &Hash) -> Option { match hashes.len() { 0 => None, diff --git a/src/error.rs b/src/error.rs index e78992b95..d2067c152 100644 --- a/src/error.rs +++ b/src/error.rs @@ -336,6 +336,9 @@ pub enum Error { #[error("Contracts states monotree root missmatch: {0} - {1}")] ContractsStatesRootError(String, String), + #[error("Hashing of Monero data failed: {0}")] + MoneroHashingError(String), + // =============== // Database errors // ===============