diff --git a/bin/darkfid/genesis_block_localnet b/bin/darkfid/genesis_block_localnet
index a953cfc5f..b16d680f0 100644
--- a/bin/darkfid/genesis_block_localnet
+++ b/bin/darkfid/genesis_block_localnet
@@ -1 +1 @@
-AYa7rEMKSzoYLxJbN6SG6cSGu/o02E70pmtKI+XwxiWxAAAAAH0JGmgAAAAAAAAAAAAAAAA7PNtvmfHqHEnjNnAS1ULkbCEM4uIYpCgNuv5kw2ETCMPjhlzo1AEcT03pj4ZfNZRxh9AwSl4slZFTeM+H5fyGAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
+AYa7rEMKSzoYLxJbN6SG6cSGu/o02E70pmtKI+XwxiWxAAAAAD5GG2gAAAAAAAAAAAAAAAA7PNtvmfHqHEnjNnAS1ULkbCEM4uIYpCgNuv5kw2ETCMPjhlzo1AEcT03pj4ZfNZRxh9AwSl4slZFTeM+H5fyGAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
diff --git a/bin/darkfid/genesis_block_mainnet b/bin/darkfid/genesis_block_mainnet
index 5a73429dc..7f248cf58 100644
--- a/bin/darkfid/genesis_block_mainnet
+++ b/bin/darkfid/genesis_block_mainnet
@@ -1 +1 @@
-AYa7rEMKSzoYLxJbN6SG6cSGu/o02E70pmtKI+XwxiWxAAAAAK0JGmgAAAAAAAAAAAAAAAA7PNtvmfHqHEnjNnAS1ULkbCEM4uIYpCgNuv5kw2ETCMPjhlzo1AEcT03pj4ZfNZRxh9AwSl4slZFTeM+H5fyGAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
+AYa7rEMKSzoYLxJbN6SG6cSGu/o02E70pmtKI+XwxiWxAAAAAE9GG2gAAAAAAAAAAAAAAAA7PNtvmfHqHEnjNnAS1ULkbCEM4uIYpCgNuv5kw2ETCMPjhlzo1AEcT03pj4ZfNZRxh9AwSl4slZFTeM+H5fyGAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
diff --git a/bin/darkfid/genesis_block_testnet b/bin/darkfid/genesis_block_testnet
index f219f1da4..ae9a70649 100644
--- a/bin/darkfid/genesis_block_testnet
+++ b/bin/darkfid/genesis_block_testnet
@@ -1 +1 @@
-AYa7rEMKSzoYLxJbN6SG6cSGu/o02E70pmtKI+XwxiWxAAAAAKEJGmgAAAAAAAAAAAAAAAA7PNtvmfHqHEnjNnAS1ULkbCEM4uIYpCgNuv5kw2ETCMPjhlzo1AEcT03pj4ZfNZRxh9AwSl4slZFTeM+H5fyGAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
+AYa7rEMKSzoYLxJbN6SG6cSGu/o02E70pmtKI+XwxiWxAAAAAEhGG2gAAAAAAAAAAAAAAAA7PNtvmfHqHEnjNnAS1ULkbCEM4uIYpCgNuv5kw2ETCMPjhlzo1AEcT03pj4ZfNZRxh9AwSl4slZFTeM+H5fyGAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
diff --git a/bin/darkfid/src/proto/protocol_sync.rs b/bin/darkfid/src/proto/protocol_sync.rs
index d2fca2412..134dd8b0d 100644
--- a/bin/darkfid/src/proto/protocol_sync.rs
+++ b/bin/darkfid/src/proto/protocol_sync.rs
@@ -107,7 +107,7 @@ pub struct HeaderSyncResponse {
impl_p2p_message!(
HeaderSyncResponse,
"headersyncresponse",
- 2341,
+ 8192, // We leave some headroom for merge mining data
1,
PROTOCOL_SYNC_METERING_CONFIGURATION
);
@@ -221,7 +221,7 @@ pub struct ForkHeadersResponse {
impl_p2p_message!(
ForkHeadersResponse,
"forkheadersresponse",
- 2341,
+ 8192, // We leave some headroom for merge mining data
1,
PROTOCOL_SYNC_METERING_CONFIGURATION
);
diff --git a/bin/darkfid/src/tests/metering.rs b/bin/darkfid/src/tests/metering.rs
index 86789696c..e96b4027b 100644
--- a/bin/darkfid/src/tests/metering.rs
+++ b/bin/darkfid/src/tests/metering.rs
@@ -33,9 +33,9 @@ fn darkfid_protocols_metering() {
const U32_LEN: usize = 4;
const VARINT_LEN: usize = 1;
const HEADER_HASH_LEN: usize = 32;
- // Header = U8_LEN + HEADER_HASH_LEN + U32_LEN + U64_LEN + U64_LEN + (U8_LEN * 32) + STATE_HASH_LEN =
- // 1 + 32 + 4 + 8 + 8 + (1 * 32) = 53 + 32 + 32 = 117
- const HEADER_LEN: usize = 117;
+ // Header = U8_LEN + HEADER_HASH_LEN + U32_LEN + U64_LEN + U64_LEN + (U8_LEN * 32) + STATE_HASH_LEN + ENUM_LEN =
+ // 1 + 32 + 4 + 8 + 8 + (1 * 32) + 1 = 53 + 32 + 32 + 1 = 118
+ const HEADER_LEN: usize = 118;
// Generate a dummy `Header`.
// Its bytes vector length is constant.
@@ -103,7 +103,7 @@ fn darkfid_protocols_metering() {
// Based on length size/type, this can add from 1(u8) to 8(u64) bytes.
// Since `BATCH` is 20, its `VarInt` will be represented as a u8,
// adding an extra byte.
- // Length = (BATCH * HEADER_LEN) + VARINT_LEN = (20 * 117) + 1 = 2340 + 1 = 2341
+ // Length = (BATCH * HEADER_LEN) + VARINT_LEN = (20 * 118) + 1 = 2360 + 1 = 2361
assert_eq!(serialize(&header_sync_response).len(), (BATCH * HEADER_LEN) + VARINT_LEN);
// Protocol sync `SyncRequest` is limited by `BATCH` so it has a
@@ -158,7 +158,7 @@ fn darkfid_protocols_metering() {
// constant max bytes length limit.
let fork_headers_response = ForkHeadersResponse { headers: vec![header; BATCH] };
// Don't forget the extra byte from `Vec` length.
- // Length = (BATCH * HEADER_LEN) + VARINT_LEN = (20 * 117) + 1 = 2340 + 1 = 2341
+ // Length = (BATCH * HEADER_LEN) + VARINT_LEN = (20 * 118) + 1 = 2360 + 1 = 2361
assert_eq!(serialize(&fork_headers_response).len(), (BATCH * HEADER_LEN) + VARINT_LEN);
// Protocol sync `ForkProposalsRequest` is limited by `BATCH` so it has a
diff --git a/bin/explorer/explorerd/src/service/blocks.rs b/bin/explorer/explorerd/src/service/blocks.rs
index 999356214..517b3302f 100644
--- a/bin/explorer/explorerd/src/service/blocks.rs
+++ b/bin/explorer/explorerd/src/service/blocks.rs
@@ -51,6 +51,8 @@ pub struct BlockRecord {
pub transactions_root: String,
/// Contracts states Monotree(SMT) root this block commits to
pub state_root: String,
+ /// Block Proof of Work type
+ pub pow_data: String,
/// Block producer signature
pub signature: Signature,
}
@@ -67,6 +69,7 @@ impl BlockRecord {
JsonValue::Number(self.nonce as f64),
JsonValue::String(self.transactions_root.clone()),
JsonValue::String(self.state_root.clone()),
+ JsonValue::String(self.pow_data.clone()),
JsonValue::String(format!("{:?}", self.signature)),
])
}
@@ -83,6 +86,7 @@ impl From<&BlockInfo> for BlockRecord {
nonce: block.header.nonce,
transactions_root: block.header.transactions_root.to_string(),
state_root: blake3::Hash::from_bytes(block.header.state_root).to_string(),
+ pow_data: format!("{:?}", block.header.pow_data),
signature: block.signature,
}
}
diff --git a/doc/src/arch/consensus.md b/doc/src/arch/consensus.md
index 291a51d3a..f8daa540e 100644
--- a/doc/src/arch/consensus.md
+++ b/doc/src/arch/consensus.md
@@ -262,6 +262,7 @@ the tx in Bitcoin with the most outputs has 2501.
| `nonce` | `u64` | The block's nonce value |
| `transactions_root` | `[u8; 32]` | Merkle tree root of the block's transactions hashes |
| `state_root` | `[u8; 32]` | Contracts states Monotree(SMT) root the block commits to |
+| `pow_data` | `Enum` | Block Proof of Work type |
## Block
diff --git a/src/blockchain/header_store.rs b/src/blockchain/header_store.rs
index d310317c7..e7fe1ce62 100644
--- a/src/blockchain/header_store.rs
+++ b/src/blockchain/header_store.rs
@@ -24,7 +24,7 @@ use darkfi_sdk::{
monotree::{Hash as StateHash, EMPTY_HASH},
};
#[cfg(feature = "async-serial")]
-use darkfi_serial::async_trait;
+use darkfi_serial::{async_trait, FutAsyncWriteExt};
use darkfi_serial::{deserialize, serialize, Encodable, SerialDecodable, SerialEncodable};
use sled_overlay::{
serial::{parse_record, parse_u32_key_record},
@@ -33,10 +33,20 @@ use sled_overlay::{
use crate::{util::time::Timestamp, Error, Result};
-use super::SledDbOverlayPtr;
+use super::{monero::MoneroPowData, SledDbOverlayPtr};
-#[derive(Copy, Clone, Debug, Eq, PartialEq, SerialEncodable, SerialDecodable)]
-// We have to introduce a type rather than using an alias so we can restrict API access
+/// Struct representing the Proof of Work used in a block.
+#[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
+#[allow(clippy::large_enum_variant)]
+pub enum PowData {
+ /// Native Darkfi PoW
+ Darkfi,
+ /// Monero merge mining PoW
+ Monero(MoneroPowData),
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq, SerialEncodable, SerialDecodable)]
+// We have to introduce a type rather than using an alias so we can restrict API access.
pub struct HeaderHash(pub [u8; 32]);
impl HeaderHash {
@@ -69,7 +79,7 @@ impl fmt::Display for HeaderHash {
}
/// This struct represents a tuple of the form (version, previous, height, timestamp, nonce, merkle_tree).
-#[derive(Debug, Clone, PartialEq, Eq, SerialEncodable, SerialDecodable)]
+#[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
pub struct Header {
/// Block version
pub version: u8,
@@ -85,17 +95,31 @@ pub struct Header {
pub transactions_root: MerkleNode,
/// Contracts states Monotree(SMT) root this block commits to
pub state_root: StateHash,
+ /// Block Proof of Work type
+ pub pow_data: PowData,
}
impl Header {
+ /// Generates a new header with default transactions and state root,
+ /// using Darkfi native Proof of Work data.
pub fn new(previous: HeaderHash, height: u32, timestamp: Timestamp, nonce: u64) -> Self {
let version = block_version(height);
let transactions_root = MerkleTree::new(1).root(0).unwrap();
let state_root = *EMPTY_HASH;
- Self { version, previous, height, timestamp, nonce, transactions_root, state_root }
+ let pow_data = PowData::Darkfi;
+ Self {
+ version,
+ previous,
+ height,
+ timestamp,
+ nonce,
+ transactions_root,
+ state_root,
+ pow_data,
+ }
}
- /// Compute the header's hash
+ /// Compute the header's hash.
pub fn hash(&self) -> HeaderHash {
let mut hasher = blake3::Hasher::new();
@@ -106,10 +130,28 @@ impl Header {
HeaderHash(hasher.finalize().into())
}
+
+ /// Compute the header's template hash, which excludes its Proof of Work data.
+ pub fn template_hash(&self) -> HeaderHash {
+ let mut hasher = blake3::Hasher::new();
+
+ // Blake3 hasher .update() method never fails.
+ // This call returns a Result due to how the Write trait is specified.
+ // Calling unwrap() here should be safe.
+ self.version.encode(&mut hasher).expect("blake3 hasher");
+ self.previous.encode(&mut hasher).expect("blake3 hasher");
+ self.height.encode(&mut hasher).expect("blake3 hasher");
+ self.timestamp.encode(&mut hasher).expect("blake3 hasher");
+ self.nonce.encode(&mut hasher).expect("blake3 hasher");
+ self.transactions_root.encode(&mut hasher).expect("blake3 hasher");
+ self.state_root.encode(&mut hasher).expect("blake3 hasher");
+
+ HeaderHash(hasher.finalize().into())
+ }
}
impl Default for Header {
- /// Represents the genesis header on current timestamp
+ /// Represents the genesis header on current timestamp.
fn default() -> Self {
Header::new(
HeaderHash::new(blake3::hash(b"Let there be dark!").into()),
@@ -123,7 +165,7 @@ impl Default for Header {
impl fmt::Display for Header {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = format!(
- "{} {{\n\t{}: {}\n\t{}: {}\n\t{}: {}\n\t{}: {}\n\t{}: {}\n\t{}: {}\n\t{}: {}\n\t{}: {}\n}}",
+ "{} {{\n\t{}: {}\n\t{}: {}\n\t{}: {}\n\t{}: {}\n\t{}: {}\n\t{}: {}\n\t{}: {}\n\t{}: {}\n\t{}: {:?}\n}}",
"Header",
"Hash",
self.hash(),
@@ -141,6 +183,8 @@ impl fmt::Display for Header {
self.transactions_root,
"State Root",
blake3::Hash::from_bytes(self.state_root),
+ "Proof of Work data",
+ self.pow_data,
);
write!(f, "{}", s)
diff --git a/src/blockchain/monero/mod.rs b/src/blockchain/monero/mod.rs
index 9432112b0..a53388d8d 100644
--- a/src/blockchain/monero/mod.rs
+++ b/src/blockchain/monero/mod.rs
@@ -16,7 +16,10 @@
* along with this program. If not, see .
*/
-use std::io::{self, Cursor, Error, Read, Write};
+use std::{
+ fmt,
+ io::{self, Cursor, Error, Read, Write},
+};
#[cfg(feature = "async-serial")]
use darkfi_serial::{async_trait, AsyncDecodable, AsyncEncodable, AsyncRead, AsyncWrite};
@@ -62,6 +65,22 @@ pub struct MoneroPowData {
pub aux_chain_merkle_proof: MerkleProof,
}
+impl fmt::Debug for MoneroPowData {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut digest = [0u8; 32];
+ self.coinbase_tx_hasher.clone().finalize(&mut digest);
+ f.debug_struct("MoneroPowData")
+ .field("header", &self.header)
+ .field("randomx_key", &self.randomx_key)
+ .field("transaction_count", &self.transaction_count)
+ .field("merkle_root", &self.merkle_root)
+ .field("coinbase_merkle_proof", &self.coinbase_merkle_proof)
+ .field("coinbase_tx_extra", &self.coinbase_tx_extra)
+ .field("aux_chain_merkle_proof", &self.aux_chain_merkle_proof)
+ .finish()
+ }
+}
+
impl Encodable for MoneroPowData {
fn encode(&self, s: &mut S) -> io::Result {
let mut n = 0;
diff --git a/src/validator/pow.rs b/src/validator/pow.rs
index c41ffd324..28b58bd2e 100644
--- a/src/validator/pow.rs
+++ b/src/validator/pow.rs
@@ -266,6 +266,7 @@ impl PoWModule {
}
/// Verify provided block corresponds to next mine target.
+ // TODO: Verify depending on block Proof of Work data
pub fn verify_block_hash(&self, block: &BlockInfo) -> Result<()> {
let verifier_setup = Instant::now();
diff --git a/src/validator/verification.rs b/src/validator/verification.rs
index 784869839..08de471cc 100644
--- a/src/validator/verification.rs
+++ b/src/validator/verification.rs
@@ -36,8 +36,8 @@ use smol::io::Cursor;
use crate::{
blockchain::{
- block_store::append_tx_to_merkle_tree, BlockInfo, Blockchain, BlockchainOverlayPtr,
- HeaderHash,
+ block_store::append_tx_to_merkle_tree, header_store::PowData, BlockInfo, Blockchain,
+ BlockchainOverlayPtr, HeaderHash,
},
error::TxVerifyFailed,
runtime::vm_runtime::Runtime,
@@ -75,6 +75,12 @@ pub async fn verify_genesis_block(
return Err(Error::BlockIsInvalid(block_hash))
}
+ // Block must use Darkfi native Proof of Work data
+ match block.header.pow_data {
+ PowData::Darkfi => { /* do nothing */ }
+ _ => return Err(Error::BlockIsInvalid(block_hash)),
+ }
+
// Verify transactions vector contains at least one(producers) transaction
if block.txs.is_empty() {
return Err(Error::BlockContainsNoTransactions(block_hash))