mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-08 22:28:12 -05:00
script/research/powdata: moved into blockchain/monero
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
|
||||
2
script/research/powdata/.gitignore
vendored
2
script/research/powdata/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
target/
|
||||
Cargo.lock
|
||||
@@ -1,15 +0,0 @@
|
||||
[package]
|
||||
name = "xmrpowdata"
|
||||
version = "0.4.1"
|
||||
authors = ["Dyne.org foundation <foundation@dyne.org>"]
|
||||
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" }
|
||||
@@ -1,5 +0,0 @@
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum MergeMineError {
|
||||
#[error("Hashing of Monero data failed: {0}")]
|
||||
HashingError(String),
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
107
src/blockchain/monero/keccak.rs
Normal file
107
src/blockchain/monero/keccak.rs
Normal file
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<W: Write>(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<R: Read>(reader: &mut R) -> Result<Keccak> {
|
||||
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<u8> {
|
||||
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);
|
||||
}
|
||||
156
src/blockchain/monero/merkle_proof.rs
Normal file
156
src/blockchain/monero/merkle_proof.rs
Normal file
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<Hash>,
|
||||
path_bitmap: u32,
|
||||
}
|
||||
|
||||
impl Encodable for MerkleProof {
|
||||
fn encode<S: Write>(&self, s: &mut S) -> Result<usize> {
|
||||
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: Read>(d: &mut D) -> Result<Self> {
|
||||
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<Hash>, path_bitmap: u32) -> Option<Self> {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<S: Write>(&self, s: &mut S) -> io::Result<usize> {
|
||||
fn encode<S: Write>(&self, s: &mut S) -> Result<usize> {
|
||||
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: Read>(d: &mut D) -> io::Result<Self> {
|
||||
let header = monero::BlockHeader::consensus_decode(d)
|
||||
.map_err(|_| io::Error::other("Invalid XMR header"))?;
|
||||
fn decode<D: Read>(d: &mut D) -> Result<Self> {
|
||||
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<u8> =
|
||||
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<W: Write>(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<R: Read>(reader: &mut R) -> io::Result<Keccak> {
|
||||
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<u8> {
|
||||
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);
|
||||
}
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<usize, MergeMineError> {
|
||||
#[allow(unused)]
|
||||
fn tree_hash_count(count: usize) -> Result<usize> {
|
||||
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<usize, MergeMineError> {
|
||||
}
|
||||
|
||||
/// Tree hash algorithm in Monero
|
||||
pub fn tree_hash(hashes: &[Hash]) -> Result<Hash, MergeMineError> {
|
||||
#[allow(unused)]
|
||||
pub fn tree_hash(hashes: &[Hash]) -> Result<Hash> {
|
||||
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<Hash, MergeMineError> {
|
||||
}
|
||||
|
||||
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<Hash, MergeMineError> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The Monero Merkle proof
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MerkleProof {
|
||||
branch: Vec<Hash>,
|
||||
path_bitmap: u32,
|
||||
}
|
||||
|
||||
impl Encodable for MerkleProof {
|
||||
fn encode<S: Write>(&self, s: &mut S) -> io::Result<usize> {
|
||||
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: Read>(d: &mut D) -> io::Result<Self> {
|
||||
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<Hash>, path_bitmap: u32) -> Option<Self> {
|
||||
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<MerkleProof> {
|
||||
match hashes.len() {
|
||||
0 => None,
|
||||
@@ -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
|
||||
// ===============
|
||||
|
||||
Reference in New Issue
Block a user