From c847febc516f4cfb576495e0190b0853d70dff48 Mon Sep 17 00:00:00 2001 From: aggstam Date: Fri, 6 Oct 2023 17:38:44 +0300 Subject: [PATCH] validator/consensus: fork/block ranking implemented --- src/blockchain/block_store.rs | 48 +++++++++++++++++++++++++++++++++-- src/error.rs | 3 +++ src/validator/consensus.rs | 43 +++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 2 deletions(-) diff --git a/src/blockchain/block_store.rs b/src/blockchain/block_store.rs index b21f4957a..2e0907f94 100644 --- a/src/blockchain/block_store.rs +++ b/src/blockchain/block_store.rs @@ -16,15 +16,19 @@ * along with this program. If not, see . */ +use std::io::Cursor; + use darkfi_sdk::{ blockchain::Slot, - crypto::schnorr::Signature, + crypto::{ecvrf::VrfProof, pasta_prelude::PrimeField, schnorr::Signature}, pasta::{group::ff::FromUniformBytes, pallas}, }; #[cfg(feature = "async-serial")] use darkfi_serial::async_trait; -use darkfi_serial::{deserialize, serialize, Encodable, SerialDecodable, SerialEncodable}; +use darkfi_serial::{ + deserialize, serialize, Decodable, Encodable, SerialDecodable, SerialEncodable, +}; use crate::{tx::Transaction, Error, Result}; @@ -143,6 +147,46 @@ impl BlockInfo { Ok(()) } + + /// Compute block's rank, assuming the block is valid. + pub fn rank(&self) -> Result { + // Genesis block has rank 0 + if self.header.height == 0 { + return Ok(0) + } + + // Extract VRF proof from the producer transaction + let tx = self.txs.last().unwrap(); + let data = &tx.calls[0].data; + let position = match self.header.version { + // PoW uses MoneyPoWRewardParamsV1 + 1 => 563, + // PoS uses ConsensusProposalParamsV1 + 2 => 490, + _ => return Err(Error::BlockVersionIsInvalid(self.header.version)), + }; + let mut decoder = Cursor::new(&data); + decoder.set_position(position); + let vrf_proof: VrfProof = Decodable::decode(&mut decoder)?; + + // Compute nonce u64 + let mut nonce = [0u8; 8]; + nonce.copy_from_slice(&self.header.nonce.to_repr()[..8]); + let nonce = u64::from_be_bytes(nonce); + + // Compute VRF u64 + let mut vrf = [0u8; 64]; + vrf[..blake3::OUT_LEN].copy_from_slice(vrf_proof.hash_output().as_bytes()); + let vrf_pallas = pallas::Base::from_uniform_bytes(&vrf); + let mut vrf = [0u8; 8]; + vrf.copy_from_slice(&vrf_pallas.to_repr()[..8]); + let vrf = u64::from_be_bytes(vrf); + + // Finally, compute the rank + let rank = nonce % vrf; + + Ok(rank) + } } /// [`Block`] sled tree diff --git a/src/error.rs b/src/error.rs index 813936193..8d663b1e7 100644 --- a/src/error.rs +++ b/src/error.rs @@ -279,6 +279,9 @@ pub enum Error { #[error("State transition failed")] StateTransitionError, + #[error("No forks exist")] + ForksNotFound, + #[error("Check if proposal extends any existing fork chains failed")] ExtendedChainIndexNotFound, diff --git a/src/validator/consensus.rs b/src/validator/consensus.rs index 1089eca39..cd4cbf9b2 100644 --- a/src/validator/consensus.rs +++ b/src/validator/consensus.rs @@ -286,6 +286,29 @@ impl Consensus { Ok(()) } + /// Auxiliary function to find current best ranked fork. + pub fn best_fork_index(&self) -> Result { + // Check if node has any forks + if self.forks.is_empty() { + return Err(Error::ForksNotFound) + } + + // Find the best ranked fork + let mut best = 0; + let mut index = 0; + for (f_index, fork) in self.forks.iter().enumerate() { + let rank = fork.rank()?; + if rank <= best { + continue + } + + best = rank; + index = f_index; + } + + Ok(index) + } + /// Given a proposal, find the index of the fork chain it extends, along with the specific /// extended proposal index. fn find_extended_fork_index(&self, proposal: &Proposal) -> Result<(usize, usize)> { @@ -598,6 +621,26 @@ impl Fork { Ok(()) } + /// Auxiliarry function to compute fork's rank, assuming all proposals are valid. + pub fn rank(&self) -> Result { + // If the fork is empty its rank is 0 + if self.proposals.is_empty() { + return Ok(0) + } + + // Retrieve the sum of all fork proposals ranks + let mut sum = 0; + let proposals = self.overlay.lock().unwrap().get_blocks_by_hash(&self.proposals)?; + for proposal in &proposals { + sum += proposal.rank()?; + } + + // Use fork(proposals) length as a multiplier to compute the actual fork rank + let rank = proposals.len() as u64 * sum; + + Ok(rank) + } + /// Auxiliary function to create a full clone using BlockchainOverlay::full_clone. /// Changes to this copy don't affect original fork overlay records, since underlying /// overlay pointer have been updated to the cloned one.