validator/consensus: fork/block ranking implemented

This commit is contained in:
aggstam
2023-10-06 17:38:44 +03:00
parent 15adf6f9cb
commit c847febc51
3 changed files with 92 additions and 2 deletions

View File

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

View File

@@ -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,

View File

@@ -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.