فهرست منبع

research/powdata: Implement (de)serialization for MoneroPowData

parazyd 1 سال پیش
والد
کامیت
9fa1476a0a
3فایلهای تغییر یافته به همراه185 افزوده شده و 2 حذف شده
  1. 2 0
      script/research/powdata/Cargo.toml
  2. 143 1
      script/research/powdata/src/lib.rs
  3. 40 1
      script/research/powdata/src/merkle_tree.rs

+ 2 - 0
script/research/powdata/Cargo.toml

@@ -11,3 +11,5 @@ edition = "2021"
 monero = "0.21.0"
 tiny-keccak = { version = "2.0.2", features = ["keccak"] }
 thiserror = "2.0.12"
+
+darkfi-serial = { path = "../../../src/serial" }

+ 143 - 1
script/research/powdata/src/lib.rs

@@ -1,6 +1,9 @@
+use std::io::{self, Read, Write};
+
+use darkfi_serial::{Decodable, Encodable};
 use monero::{
     blockdata::transaction::RawExtraField,
-    consensus::Encodable,
+    consensus::{Decodable as XmrDecodable, Encodable as XmrEncodable},
     cryptonote::hash::Hashable,
     util::ringct::{RctSigBase, RctType},
 };
@@ -33,6 +36,61 @@ pub struct MoneroPowData {
     pub aux_chain_merkle_proof: MerkleProof,
 }
 
+impl Encodable for MoneroPowData {
+    fn encode<S: Write>(&self, s: &mut S) -> io::Result<usize> {
+        let mut n = 0;
+
+        n += self.header.consensus_encode(s)?;
+        n += self.randomx_key.encode(s)?;
+        n += self.transaction_count.encode(s)?;
+        n += self.merkle_root.as_fixed_bytes().encode(s)?;
+
+        // This is an incomplete hasher. Dump it from memory
+        // and write it down. We can restore it the same way.
+        let buf = keccak_to_bytes(&self.coinbase_tx_hasher);
+        n += buf.encode(s)?;
+
+        n += self.coinbase_tx_extra.0.encode(s)?;
+        n += self.aux_chain_merkle_proof.encode(s)?;
+
+        Ok(n)
+    }
+}
+
+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"))?;
+
+        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 coinbase_merkle_proof: MerkleProof = Decodable::decode(d)?;
+
+        let buf: Vec<u8> = Decodable::decode(d)?;
+        let coinbase_tx_hasher = keccak_from_bytes(&buf);
+
+        let coinbase_tx_extra: Vec<u8> = Decodable::decode(d)?;
+        let coinbase_tx_extra = RawExtraField(coinbase_tx_extra);
+
+        let aux_chain_merkle_proof: MerkleProof = Decodable::decode(d)?;
+
+        Ok(Self {
+            header,
+            randomx_key,
+            transaction_count,
+            merkle_root,
+            coinbase_merkle_proof,
+            coinbase_tx_hasher,
+            coinbase_tx_extra,
+            aux_chain_merkle_proof,
+        })
+    }
+}
+
 impl MoneroPowData {
     /// Returns true if the coinbase Merkle proof produces the `merkle_root` hash.
     pub fn is_coinbase_valid_merkle_root(&self) -> bool {
@@ -63,3 +121,87 @@ impl MoneroPowData {
         (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);
+}

+ 40 - 1
script/research/powdata/src/merkle_tree.rs

@@ -1,4 +1,10 @@
-use monero::Hash;
+use std::io::{self, Read, Write};
+
+use darkfi_serial::{Decodable, Encodable, ReadExt};
+use monero::{
+    consensus::{Decodable as XmrDecodable, Encodable as XmrEncodable},
+    Hash,
+};
 
 use crate::error::MergeMineError;
 
@@ -99,6 +105,39 @@ pub struct MerkleProof {
     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 {