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.