Просмотр исходного кода

research/pow: High level RandomX mining layout.

parazyd 2 лет назад
Родитель
Сommit
2c46a1d9fd
3 измененных файлов с 182 добавлено и 0 удалено
  1. 2 0
      script/research/pow/.gitignore
  2. 14 0
      script/research/pow/Cargo.toml
  3. 166 0
      script/research/pow/src/main.rs

+ 2 - 0
script/research/pow/.gitignore

@@ -0,0 +1,2 @@
+target/
+Cargo.lock

+ 14 - 0
script/research/pow/Cargo.toml

@@ -0,0 +1,14 @@
+[package]
+name = "pow"
+version = "0.1.0"
+edition = "2021"
+
+[workspace]
+
+[dependencies]
+randomx = {git = "https://github.com/darkrenaissance/RandomX"}
+darkfi-serial = {path = "../../../src/serial"}
+darkfi-sdk = {path = "../../../src/sdk", features = ["async"]}
+darkfi = {path = "../../../", features = ["util"]}
+
+blake3 = "1.4.1"

+ 166 - 0
script/research/pow/src/main.rs

@@ -0,0 +1,166 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 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::{sync::Arc, time::Instant};
+
+use darkfi::{util::time::Timestamp, Result};
+use darkfi_sdk::{
+    crypto::MerkleTree,
+    pasta::{group::ff::FromUniformBytes, pallas},
+};
+use darkfi_serial::{async_trait, Encodable, SerialDecodable, SerialEncodable};
+use randomx::{RandomXCache, RandomXDataset, RandomXFlags, RandomXVM};
+
+const GENESIS: &[u8] = b"genesis";
+const DIFFICULTY: usize = 1;
+
+#[derive(SerialEncodable, SerialDecodable)]
+struct Transaction(Vec<u8>);
+
+impl Transaction {
+    fn hash(&self) -> Result<blake3::Hash> {
+        let mut hasher = blake3::Hasher::new();
+        self.encode(&mut hasher)?;
+        Ok(hasher.finalize())
+    }
+}
+
+#[derive(SerialEncodable, SerialDecodable)]
+struct BlockHeader {
+    nonce: u32,
+    previous_hash: blake3::Hash,
+    timestamp: Timestamp,
+    txtree: MerkleTree,
+}
+
+#[derive(SerialEncodable, SerialDecodable)]
+struct Block {
+    header: BlockHeader,
+    transactions: Vec<Transaction>,
+}
+
+impl Block {
+    fn hash(&self) -> Result<blake3::Hash> {
+        let mut len = 0;
+        let mut hasher = blake3::Hasher::new();
+
+        len += self.header.encode(&mut hasher)?;
+        len += self.header.txtree.root(0).unwrap().encode(&mut hasher)?;
+        len += self.transactions.len().encode(&mut hasher)?;
+
+        len.encode(&mut hasher)?;
+
+        Ok(hasher.finalize())
+    }
+
+    fn pow_input(&self) -> Result<[u8; blake3::OUT_LEN]> {
+        let mut hasher = blake3::Hasher::new();
+
+        let _ = self.header.encode(&mut hasher)?;
+        let _ = self.header.txtree.root(0).unwrap().encode(&mut hasher)?;
+        let _ = self.transactions.len().encode(&mut hasher)?;
+
+        Ok(hasher.finalize().into())
+    }
+
+    fn insert_tx(&mut self, tx: &Transaction) -> Result<()> {
+        let mut buf = [0u8; 64];
+        buf[..blake3::OUT_LEN].copy_from_slice(tx.hash()?.as_bytes());
+        let leaf = pallas::Base::from_uniform_bytes(&buf);
+        self.header.txtree.append(leaf.into());
+        Ok(())
+    }
+}
+
+fn main() -> Result<()> {
+    // Construct the genesis block
+    let genesis_hash = blake3::hash(GENESIS);
+    let mut genesis_block = Block {
+        header: BlockHeader {
+            nonce: 0,
+            previous_hash: genesis_hash,
+            timestamp: Timestamp::current_time(),
+            txtree: MerkleTree::new(100),
+        },
+        transactions: vec![],
+    };
+
+    let genesis_tx = Transaction(vec![1, 3, 3, 7]);
+    genesis_block.insert_tx(&genesis_tx)?;
+
+    // Get initial PoW input
+    let pow_input = genesis_block.pow_input()?;
+
+    // This is single-threaded mining, but check darkrenaissance/RandomX/examples/
+    // for multi-threaded ops.
+    let miner_setup = Instant::now();
+    let flags = RandomXFlags::default() | RandomXFlags::FULLMEM;
+    let dataset = Arc::new(RandomXDataset::new(flags, &pow_input, 1).unwrap());
+    let vm = RandomXVM::new_fast(flags, &dataset).unwrap();
+
+    // The miner creates a block
+    let mut miner_block = Block {
+        header: BlockHeader {
+            nonce: 0,
+            previous_hash: genesis_block.hash()?,
+            timestamp: Timestamp::current_time(),
+            txtree: MerkleTree::new(100),
+        },
+        transactions: vec![],
+    };
+    let tx0 = Transaction(vec![0, 3, 1, 2]);
+    let tx1 = Transaction(vec![1, 2, 1, 0]);
+    miner_block.insert_tx(&tx0)?;
+    miner_block.insert_tx(&tx1)?;
+    println!("Miner setup time: {:?}", miner_setup.elapsed());
+
+    // Melt the CPU
+    let mining_time = Instant::now();
+    loop {
+        let out_hash = vm.hash(miner_block.hash()?.as_bytes());
+        let mut success = true;
+        for i in 0..DIFFICULTY {
+            if out_hash[i] != 0x00 {
+                success = false;
+            }
+        }
+
+        if success {
+            break
+        }
+
+        miner_block.header.nonce += 1;
+    }
+    println!("Mining time: {:?}", mining_time.elapsed());
+
+    // Verify
+    let verifier_setup = Instant::now();
+    let flags = RandomXFlags::default();
+    let cache = RandomXCache::new(flags, &pow_input).unwrap();
+    let vm = RandomXVM::new(flags, &cache).unwrap();
+    println!("Verifier setup time: {:?}", verifier_setup.elapsed());
+
+    let verification_time = Instant::now();
+    let out_hash = vm.hash(miner_block.hash()?.as_bytes());
+    for i in 0..DIFFICULTY {
+        assert!(out_hash[i] == 0x00);
+    }
+    println!("Verification time: {:?}", verification_time.elapsed());
+
+    Ok(())
+}