mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-04-28 03:00:18 -04:00
[consensus] reorg is_lead, and sigmas
This commit is contained in:
@@ -39,6 +39,7 @@ use crate::{
|
||||
Error, Result,
|
||||
};
|
||||
use darkfi_serial::{Decodable, Encodable, SerialDecodable, SerialEncodable};
|
||||
use log::info;
|
||||
|
||||
pub const MERKLE_DEPTH_LEADCOIN: usize = 32;
|
||||
pub const MERKLE_DEPTH: u8 = 32;
|
||||
@@ -92,12 +93,6 @@ pub struct LeadCoin {
|
||||
pub y_mu: pallas::Base,
|
||||
/// Leader election nonce derived from eta at onset of epoch
|
||||
pub rho_mu: pallas::Base,
|
||||
/// First coefficient in 1-term T (target function) approximation.
|
||||
/// NOTE: sigma1 and sigma2 are not the capital sigma from the paper, but
|
||||
/// the whole coefficient multiplied with absolute stake.
|
||||
pub sigma1: pallas::Base,
|
||||
/// Second coefficient in 2-term T (target function) approximation.
|
||||
pub sigma2: pallas::Base,
|
||||
/// Coin's secret key
|
||||
pub secret_key: SecretKey,
|
||||
}
|
||||
@@ -107,10 +102,6 @@ impl LeadCoin {
|
||||
pub fn new(
|
||||
// emulation of global random oracle output from previous epoch randomness.
|
||||
eta: pallas::Base,
|
||||
// First coefficient in 1-term T (target function) approximation.
|
||||
sigma1: pallas::Base,
|
||||
// Second coefficient in 2-term T (target function) approximation.
|
||||
sigma2: pallas::Base,
|
||||
// Stake value
|
||||
value: u64,
|
||||
// Slot index in the epoch
|
||||
@@ -211,8 +202,6 @@ impl LeadCoin {
|
||||
coin2_blind,
|
||||
y_mu,
|
||||
rho_mu,
|
||||
sigma1,
|
||||
sigma2,
|
||||
secret_key,
|
||||
}
|
||||
}
|
||||
@@ -302,6 +291,27 @@ impl LeadCoin {
|
||||
rho
|
||||
}
|
||||
|
||||
pub fn is_leader(&self, sigma1: pallas::Base, sigma2: pallas::Base) -> bool {
|
||||
let y_exp = [self.coin1_sk_root.inner(), self.nonce];
|
||||
let y_exp_hash = poseidon_hash(y_exp);
|
||||
let y_coords = pedersen_commitment_base(y_exp_hash, mod_r_p(self.y_mu))
|
||||
.to_affine()
|
||||
.coordinates()
|
||||
.unwrap();
|
||||
|
||||
let y_coords = [*y_coords.x(), *y_coords.y()];
|
||||
let y = poseidon_hash(y_coords);
|
||||
|
||||
let value = pallas::Base::from(self.value);
|
||||
let target = sigma1 * value + sigma2 * value * value;
|
||||
|
||||
info!("Consensus::is_leader(): y = {:?}", y);
|
||||
info!("Consensus::is_leader(): T = {:?}", target);
|
||||
|
||||
let first_winning = y < target;
|
||||
first_winning
|
||||
}
|
||||
|
||||
/// calculated derived coin commitment
|
||||
pub fn derived_commitment(&self, blind: pallas::Scalar) -> pallas::Point {
|
||||
let pk = self.pk();
|
||||
@@ -334,7 +344,10 @@ impl LeadCoin {
|
||||
}
|
||||
|
||||
/// Try to create a ZK proof of consensus leadership
|
||||
pub fn create_lead_proof(&self, pk: &ProvingKey) -> Result<Proof> {
|
||||
pub fn create_lead_proof(&self,
|
||||
sigma1: pallas::Base,
|
||||
sigma2: pallas::Base,
|
||||
pk: &ProvingKey) -> Result<Proof> {
|
||||
let bincode = include_bytes!("../../proof/lead.zk.bin");
|
||||
let zkbin = ZkBinary::decode(bincode)?;
|
||||
let witnesses = vec![
|
||||
@@ -351,8 +364,8 @@ impl LeadCoin {
|
||||
Witness::Scalar(Value::known(self.coin2_blind)),
|
||||
Witness::Scalar(Value::known(mod_r_p(self.rho_mu))),
|
||||
Witness::Scalar(Value::known(mod_r_p(self.y_mu))),
|
||||
Witness::Base(Value::known(self.sigma1)),
|
||||
Witness::Base(Value::known(self.sigma2)),
|
||||
Witness::Base(Value::known(sigma1)),
|
||||
Witness::Base(Value::known(sigma2)),
|
||||
];
|
||||
let circuit = ZkCircuit::new(witnesses, zkbin.clone());
|
||||
Ok(Proof::create(pk, &[circuit], &self.public_inputs(), &mut OsRng)?)
|
||||
|
||||
@@ -76,10 +76,12 @@ pub struct LeadProof {
|
||||
}
|
||||
|
||||
impl LeadProof {
|
||||
/*
|
||||
pub fn new(pk: &ProvingKey, coin: LeadCoin) -> Self {
|
||||
let proof = coin.create_lead_proof(pk).unwrap();
|
||||
Self { proof }
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn verify(&self, vk: &VerifyingKey, public_inputs: &[pallas::Base]) -> Result<()> {
|
||||
if let Err(e) = self.proof.verify(vk, public_inputs) {
|
||||
|
||||
@@ -134,6 +134,12 @@ pub struct ValidatorState {
|
||||
pub participating: Option<u64>,
|
||||
/// Wallet interface
|
||||
pub wallet: WalletPtr,
|
||||
/// nullifiers
|
||||
pub nullifiers: Vec<pallas::Base>,
|
||||
/// spent coins
|
||||
pub spent: Vec<pallas::Base>,
|
||||
/// lead coins
|
||||
pub lead: Vec<pallas::Base>,
|
||||
}
|
||||
|
||||
impl ValidatorState {
|
||||
@@ -204,6 +210,9 @@ impl ValidatorState {
|
||||
unconfirmed_txs,
|
||||
participating,
|
||||
wallet,
|
||||
nullifiers: vec![],
|
||||
spent: vec![],
|
||||
lead: vec![],
|
||||
}));
|
||||
|
||||
Ok(state)
|
||||
@@ -330,6 +339,34 @@ impl ValidatorState {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// return 2-term target approximation sigma coefficients.
|
||||
/// `epoch: absolute epoch index
|
||||
/// `slot: relative slot index
|
||||
fn sigmas(&self, epoch: u64, slot: u64) -> (pallas::Base, pallas::Base) {
|
||||
let f = Self::leadership_probability_with_all_stake().with_precision(RADIX_BITS).value();
|
||||
info!("Consensus: f: {}", f);
|
||||
|
||||
// Generate sigmas
|
||||
let total_stake = Self::total_stake_plus(epoch, slot); // Only used for fine-tuning
|
||||
|
||||
let one = Float10::from_str_native("1").unwrap().with_precision(RADIX_BITS).value();
|
||||
let two = Float10::from_str_native("2").unwrap().with_precision(RADIX_BITS).value();
|
||||
let field_p = Float10::from_str_native(P).unwrap().with_precision(RADIX_BITS).value();
|
||||
let total_sigma =
|
||||
Float10::try_from(total_stake).unwrap().with_precision(RADIX_BITS).value();
|
||||
|
||||
let x = one - f;
|
||||
let c = x.ln();
|
||||
|
||||
let sigma1_fbig = c.clone() / total_sigma.clone() * field_p.clone();
|
||||
let sigma1 = fbig2base(sigma1_fbig);
|
||||
|
||||
let sigma2_fbig = (c / total_sigma).powf(two.clone()) * (field_p / two);
|
||||
let sigma2 = fbig2base(sigma2_fbig);
|
||||
(sigma1, sigma2)
|
||||
|
||||
}
|
||||
|
||||
/// Generate epoch-competing coins
|
||||
async fn create_epoch_coins(
|
||||
&self,
|
||||
@@ -338,29 +375,7 @@ impl ValidatorState {
|
||||
slot: u64,
|
||||
) -> Result<Vec<Vec<LeadCoin>>> {
|
||||
info!("Consensus: Creating coins for epoch: {}", epoch);
|
||||
|
||||
// Retrieve previous epoch-competing coins' frequency
|
||||
let frequency = Self::get_frequency().with_precision(RADIX_BITS).value();
|
||||
info!("Consensus: Previous epoch frequency: {}", frequency);
|
||||
|
||||
// Generate sigmas
|
||||
let total_stake = Self::total_stake(epoch, slot); // Only used for fine-tuning
|
||||
|
||||
let one = Float10::from_str_native("1").unwrap().with_precision(RADIX_BITS).value();
|
||||
let two = Float10::from_str_native("2").unwrap().with_precision(RADIX_BITS).value();
|
||||
let field_p = Float10::from_str_native(P).unwrap().with_precision(RADIX_BITS).value();
|
||||
let total_sigma =
|
||||
Float10::try_from(total_stake).unwrap().with_precision(RADIX_BITS).value();
|
||||
|
||||
let x = one - frequency;
|
||||
let c = x.ln();
|
||||
|
||||
let sigma1_fbig = c.clone() / total_sigma.clone() * field_p.clone();
|
||||
let sigma1 = fbig2base(sigma1_fbig);
|
||||
|
||||
let sigma2_fbig = (c / total_sigma).powf(two.clone()) * (field_p / two);
|
||||
let sigma2 = fbig2base(sigma2_fbig);
|
||||
|
||||
let (sigma1, sigma2) = self.sigmas(epoch, slot);
|
||||
self.create_coins(eta, sigma1, sigma2).await
|
||||
}
|
||||
|
||||
@@ -393,8 +408,6 @@ impl ValidatorState {
|
||||
for i in 0..EPOCH_LENGTH {
|
||||
let coin = LeadCoin::new(
|
||||
eta,
|
||||
sigma1,
|
||||
sigma2,
|
||||
LOTTERY_HEAD_START, // TODO: TESTNET: Why is this constant being used?
|
||||
i as u64,
|
||||
epoch_secrets.secret_keys[i].inner(),
|
||||
@@ -412,14 +425,26 @@ impl ValidatorState {
|
||||
Ok(coins)
|
||||
}
|
||||
|
||||
fn total_stake(epoch: u64, slot: u64) -> u64 {
|
||||
// TODO: Fix this
|
||||
// (epoch * EPOCH_LENGTH + slot + 1) * REWARD
|
||||
REWARD
|
||||
/// leadership reward, assuming constant reward
|
||||
/// TODO (res) implement reward mechanism with accord to DRK,DARK token-economics
|
||||
fn reward() -> u64 {
|
||||
REWARD
|
||||
}
|
||||
|
||||
fn get_frequency() -> Float10 {
|
||||
// TODO: Actually retrieve frequency of coins from the previous epoch.
|
||||
///TODO: impl total empty slots count.
|
||||
fn empty_slots_count() -> u64 {
|
||||
0
|
||||
}
|
||||
|
||||
/// total stake plus one.
|
||||
/// assuming constant Reward.
|
||||
fn total_stake_plus(epoch: u64, slot: u64) -> u64 {
|
||||
(epoch * EPOCH_LENGTH as u64 + slot + 1 - Self::empty_slots_count()) * Self::reward()
|
||||
}
|
||||
|
||||
/// leadership probability having all the stake.
|
||||
/// dynamically auto-tune f
|
||||
fn leadership_probability_with_all_stake() -> Float10 {
|
||||
let one = Float10::from_str_native("1").unwrap().with_precision(RADIX_BITS).value();
|
||||
let two = Float10::from_str_native("2").unwrap().with_precision(RADIX_BITS).value();
|
||||
one / two
|
||||
@@ -434,6 +459,8 @@ impl ValidatorState {
|
||||
pub fn is_slot_leader(&self) -> (bool, usize) {
|
||||
// Slot relative index
|
||||
let slot = self.relative_slot(self.current_slot());
|
||||
let epoch = self.current_epoch();
|
||||
let (sigma1, sigma2) = self.sigmas(slot, epoch);
|
||||
// Stakeholder's epoch coins
|
||||
let coins = &self.consensus.coins;
|
||||
|
||||
@@ -447,23 +474,7 @@ impl ValidatorState {
|
||||
let mut highest_stake_idx = 0;
|
||||
|
||||
for (winning_idx, coin) in competing_coins.iter().enumerate() {
|
||||
let y_exp = [coin.coin1_sk_root.inner(), coin.nonce];
|
||||
let y_exp_hash = poseidon_hash(y_exp);
|
||||
let y_coords = pedersen_commitment_base(y_exp_hash, mod_r_p(coin.y_mu))
|
||||
.to_affine()
|
||||
.coordinates()
|
||||
.unwrap();
|
||||
|
||||
let y_coords = [*y_coords.x(), *y_coords.y()];
|
||||
let y = poseidon_hash(y_coords);
|
||||
|
||||
let value = pallas::Base::from(coin.value);
|
||||
let target = coin.sigma1 * value + coin.sigma2 * value * value;
|
||||
|
||||
info!("Consensus::is_leader(): y = {:?}", y);
|
||||
info!("Consensus::is_leader(): T = {:?}", target);
|
||||
|
||||
let first_winning = y < target;
|
||||
let first_winning = coin.is_leader(sigma1, sigma2);
|
||||
if first_winning && !won {
|
||||
highest_stake_idx = winning_idx;
|
||||
}
|
||||
@@ -483,6 +494,8 @@ impl ValidatorState {
|
||||
/// chain the node is holding.
|
||||
pub fn propose(&mut self, idx: usize) -> Result<Option<BlockProposal>> {
|
||||
let slot = self.current_slot();
|
||||
let epoch = self.current_epoch();
|
||||
let (sigma1, sigma2) = self.sigmas(slot, epoch);
|
||||
let (prev_hash, index) = self.longest_chain_last_hash().unwrap();
|
||||
let unproposed_txs = self.unproposed_txs(index);
|
||||
|
||||
@@ -503,7 +516,7 @@ impl ValidatorState {
|
||||
// Generating leader proof
|
||||
let relative_slot = self.relative_slot(slot) as usize;
|
||||
let coin = self.consensus.coins[relative_slot][idx];
|
||||
let proof = coin.create_lead_proof(&self.lead_proving_key)?;
|
||||
let proof = coin.create_lead_proof(sigma1, sigma2, &self.lead_proving_key)?;
|
||||
|
||||
// Signing using coin
|
||||
let secret_key = coin.secret_key;
|
||||
|
||||
Reference in New Issue
Block a user