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))