|
@@ -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 darkfi_serial::{Decodable, Encodable};
|
|
|
use monero::{
|
|
use monero::{
|
|
@@ -6,18 +24,24 @@ use monero::{
|
|
|
consensus::{Decodable as XmrDecodable, Encodable as XmrEncodable},
|
|
consensus::{Decodable as XmrDecodable, Encodable as XmrEncodable},
|
|
|
cryptonote::hash::Hashable,
|
|
cryptonote::hash::Hashable,
|
|
|
util::ringct::{RctSigBase, RctType},
|
|
util::ringct::{RctSigBase, RctType},
|
|
|
|
|
+ BlockHeader, Hash,
|
|
|
};
|
|
};
|
|
|
use tiny_keccak::{Hasher, Keccak};
|
|
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)]
|
|
#[derive(Clone)]
|
|
|
pub struct MoneroPowData {
|
|
pub struct MoneroPowData {
|
|
|
/// Monero Header fields
|
|
/// Monero Header fields
|
|
|
- pub header: monero::BlockHeader,
|
|
|
|
|
|
|
+ pub header: BlockHeader,
|
|
|
/// RandomX VM key - length varies to a max len of 60.
|
|
/// RandomX VM key - length varies to a max len of 60.
|
|
|
/// TODO: Implement a type, or use randomx_key[0] to define len.
|
|
/// TODO: Implement a type, or use randomx_key[0] to define len.
|
|
|
pub randomx_key: [u8; 64],
|
|
pub randomx_key: [u8; 64],
|
|
@@ -25,7 +49,7 @@ pub struct MoneroPowData {
|
|
|
/// This is used to produce the blockhashing_blob.
|
|
/// This is used to produce the blockhashing_blob.
|
|
|
pub transaction_count: u16,
|
|
pub transaction_count: u16,
|
|
|
/// Transaction root
|
|
/// Transaction root
|
|
|
- pub merkle_root: monero::Hash,
|
|
|
|
|
|
|
+ pub merkle_root: Hash,
|
|
|
/// Coinbase Merkle proof hashes
|
|
/// Coinbase Merkle proof hashes
|
|
|
pub coinbase_merkle_proof: MerkleProof,
|
|
pub coinbase_merkle_proof: MerkleProof,
|
|
|
/// Incomplete hashed state of the coinbase transaction
|
|
/// Incomplete hashed state of the coinbase transaction
|
|
@@ -37,7 +61,7 @@ pub struct MoneroPowData {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
impl Encodable for 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;
|
|
let mut n = 0;
|
|
|
|
|
|
|
|
n += self.header.consensus_encode(s)?;
|
|
n += self.header.consensus_encode(s)?;
|
|
@@ -58,15 +82,15 @@ impl Encodable for MoneroPowData {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
impl Decodable 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 randomx_key: [u8; 64] = Decodable::decode(d)?;
|
|
|
let transaction_count: u16 = Decodable::decode(d)?;
|
|
let transaction_count: u16 = Decodable::decode(d)?;
|
|
|
|
|
|
|
|
let merkle_root: [u8; 32] = 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)?;
|
|
let coinbase_merkle_proof: MerkleProof = Decodable::decode(d)?;
|
|
|
|
|
|
|
@@ -101,7 +125,7 @@ impl MoneroPowData {
|
|
|
let mut prefix_hash: [u8; 32] = [0; 32];
|
|
let mut prefix_hash: [u8; 32] = [0; 32];
|
|
|
finalised_prefix_keccak.finalize(&mut prefix_hash);
|
|
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 mut finalised_keccak = Keccak::v256();
|
|
|
let rct_sig_base = RctSigBase {
|
|
let rct_sig_base = RctSigBase {
|
|
@@ -112,96 +136,12 @@ impl MoneroPowData {
|
|
|
out_pk: vec![],
|
|
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> =
|
|
let encoder_final: Vec<u8> =
|
|
|
hashes.into_iter().flat_map(|h| Vec::from(&h.to_bytes()[..])).collect();
|
|
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);
|
|
let merkle_root = self.coinbase_merkle_proof.calculate_root(&coinbase_hash);
|
|
|
(self.merkle_root == merkle_root) && self.coinbase_merkle_proof.check_coinbase_path()
|
|
(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);
|
|
|
|
|
-}
|
|
|