consensus: Use BigUint for block ranking to obtain higher resolution

This commit is contained in:
parazyd
2024-02-11 13:00:13 +01:00
parent 6916cff694
commit 68c9bc8418
3 changed files with 22 additions and 31 deletions

View File

@@ -72,9 +72,8 @@ First 2 blocks rank is equal to their nonce, since their previous previous block
producer doesn't exist, or have a `VRF` attached to their reward transaction.
For rest blocks, the rank computes as following:
1. Grab the `VRF` proof from the reward transaction of the previous previous proposal
2. Generate a `pallas::Base` from the `blake3::Hash` bytes of the proof
3. Generate a `u64` using the first 8 bytes from the `pallas::Base` of the proofs hash
4. Compute the rank: `vrf_u64` % `nonce` (If `nonce` is 0, rank is equal to `vrf_u64`)
2. Obtain a big-integer from the big endian output of the `VRF`
3. Compute the rank: `vrf.output` % `nonce` (If `nonce` is 0, rank is equal to `vrf.output`)
To calculate each fork rank, we simply sum all its block proposals ranks and multiply
that with the forks length. We use the length multiplier to give a chance of higher

View File

@@ -314,7 +314,7 @@ pub struct Fork {
/// Valid pending transaction hashes
pub mempool: Vec<blake3::Hash>,
/// Current fork rank, cached for better performance
pub rank: u64,
pub rank: BigUint,
}
impl Fork {
@@ -322,7 +322,7 @@ impl Fork {
let mempool =
blockchain.get_pending_txs()?.iter().map(|tx| blake3::hash(&serialize(tx))).collect();
let overlay = BlockchainOverlay::new(blockchain)?;
Ok(Self { overlay, module, proposals: vec![], mempool, rank: 0 })
Ok(Self { overlay, module, proposals: vec![], mempool, rank: BigUint::from(0u64) })
}
/// Auxiliary function to append a proposal and recalculate current fork rank
@@ -420,14 +420,14 @@ impl Fork {
}
/// Auxiliarry function to compute fork's rank, assuming all proposals are valid.
pub async fn rank(&self) -> Result<u64> {
pub async fn rank(&self) -> Result<BigUint> {
// If the fork is empty its rank is 0
if self.proposals.is_empty() {
return Ok(0)
return Ok(0u64.into())
}
// Retrieve the sum of all fork proposals ranks
let mut sum = 0;
let mut sum = BigUint::from(0_u64);
let proposals = self.overlay.lock().unwrap().get_blocks_by_hash(&self.proposals)?;
for proposal in &proposals {
// For block height < 3 we use the same proposal reference, since
@@ -459,7 +459,7 @@ impl Fork {
let module = self.module.clone();
let proposals = self.proposals.clone();
let mempool = self.mempool.clone();
let rank = self.rank;
let rank = self.rank.clone();
Ok(Self { overlay, module, proposals, mempool, rank })
}

View File

@@ -16,15 +16,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use darkfi_sdk::{
crypto::{
ecvrf::VrfProof, pasta_prelude::PrimeField, DAO_CONTRACT_ID, DEPLOYOOOR_CONTRACT_ID,
MONEY_CONTRACT_ID,
},
pasta::{group::ff::FromUniformBytes, pallas},
use darkfi_sdk::crypto::{
ecvrf::VrfProof, DAO_CONTRACT_ID, DEPLOYOOOR_CONTRACT_ID, MONEY_CONTRACT_ID,
};
use darkfi_serial::AsyncDecodable;
use log::info;
use num_bigint::BigUint;
use smol::io::Cursor;
use crate::{
@@ -103,10 +100,10 @@ pub async fn deploy_native_contracts(overlay: &BlockchainOverlayPtr) -> Result<(
/// Genesis block has rank 0.
/// First 2 blocks rank is equal to their nonce, since their previous
/// previous block producer doesn't exist or have a VRF.
pub async fn block_rank(block: &BlockInfo, previous_previous: &BlockInfo) -> Result<u64> {
pub async fn block_rank(block: &BlockInfo, previous_previous: &BlockInfo) -> Result<BigUint> {
// Genesis block has rank 0
if block.header.height == 0 {
return Ok(0)
return Ok(0u64.into())
}
// Grab block nonce
@@ -114,7 +111,7 @@ pub async fn block_rank(block: &BlockInfo, previous_previous: &BlockInfo) -> Res
// First 2 blocks have rank equal to their nonce
if block.header.height < 3 {
return Ok(nonce)
return Ok(nonce.into())
}
// Extract VRF proof from the previous previous producer transaction
@@ -125,16 +122,11 @@ pub async fn block_rank(block: &BlockInfo, previous_previous: &BlockInfo) -> Res
decoder.set_position(499);
let vrf_proof: VrfProof = AsyncDecodable::decode_async(&mut decoder).await?;
// 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);
// Get the VRF output as big-endian
let vrf_output = BigUint::from_bytes_be(vrf_proof.hash_output().as_bytes());
// Finally, compute the rank
let rank = if nonce != 0 { vrf % nonce } else { vrf };
let rank = if nonce != 0 { vrf_output % nonce } else { vrf_output };
Ok(rank)
}
@@ -206,24 +198,24 @@ pub fn best_forks_indexes(forks: &[Fork]) -> Result<Vec<usize>> {
}
// Find the best ranked forks
let mut best = 0;
let mut best = BigUint::from(0u64);
let mut indexes = vec![];
for (f_index, fork) in forks.iter().enumerate() {
let rank = fork.rank;
let rank = &fork.rank;
// Fork ranks lower that current best
if rank < best {
if rank < &best {
continue
}
// Fork has same rank as current best
if rank == best {
if rank == &best {
indexes.push(f_index);
continue
}
// Fork ranks higher that current best
best = rank;
best = rank.clone();
indexes = vec![f_index];
}