소스 검색

validator/consensus: fork/block ranking implemented

aggstam 2 년 전
부모
커밋
c847febc51
3개의 변경된 파일92개의 추가작업 그리고 2개의 파일을 삭제
  1. 46 2
      src/blockchain/block_store.rs
  2. 3 0
      src/error.rs
  3. 43 0
      src/validator/consensus.rs

+ 46 - 2
src/blockchain/block_store.rs

@@ -16,15 +16,19 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
+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<u64> {
+        // 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

+ 3 - 0
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,
 

+ 43 - 0
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<usize> {
+        // 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<u64> {
+        // 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.