diff --git a/src/consensus/leadcoin.rs b/src/consensus/leadcoin.rs index a782da154..78e20001c 100644 --- a/src/consensus/leadcoin.rs +++ b/src/consensus/leadcoin.rs @@ -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 { + pub fn create_lead_proof(&self, + sigma1: pallas::Base, + sigma2: pallas::Base, + pk: &ProvingKey) -> Result { 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)?) diff --git a/src/consensus/metadata.rs b/src/consensus/metadata.rs index 08a8b29e2..18df34e1d 100644 --- a/src/consensus/metadata.rs +++ b/src/consensus/metadata.rs @@ -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) { diff --git a/src/consensus/state.rs b/src/consensus/state.rs index e6a08ace1..1dca133d8 100644 --- a/src/consensus/state.rs +++ b/src/consensus/state.rs @@ -134,6 +134,12 @@ pub struct ValidatorState { pub participating: Option, /// Wallet interface pub wallet: WalletPtr, + /// nullifiers + pub nullifiers: Vec, + /// spent coins + pub spent: Vec, + /// lead coins + pub lead: Vec, } 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>> { 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> { 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;