From 233c18bcf348c52ec69e68f7e215bc237696f32c Mon Sep 17 00:00:00 2001 From: goshawk-3 Date: Thu, 4 Jul 2024 16:41:54 +0300 Subject: [PATCH 1/9] rusk: Split Block Generator reward into fixed reward and extra reward --- rusk/src/lib/chain.rs | 24 ++++++++++++++++++------ rusk/src/lib/chain/rusk.rs | 32 +++++++++++++++++++++++--------- 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/rusk/src/lib/chain.rs b/rusk/src/lib/chain.rs index 75a44b9999..1a2e47fcb2 100644 --- a/rusk/src/lib/chain.rs +++ b/rusk/src/lib/chain.rs @@ -55,19 +55,31 @@ impl RuskNode { /// Calculates the value that the coinbase notes should contain. /// /// 10% of the reward value goes to the Dusk address (rounded down). -/// 80% of the reward value goes to the Block generator (rounded up). +/// 70% of the reward value is considered fixed reward for Block Generator. +/// 10% of the reward value is considered extra reward for Block Generator. /// 10% of the reward value goes to the all validators/voters of previous block /// (rounded down). const fn coinbase_value( block_height: u64, dusk_spent: u64, -) -> (Dusk, Dusk, Dusk) { +) -> (Dusk, Dusk, Dusk, Dusk) { let value = emission_amount(block_height) + dusk_spent; - let dusk_value = value / 10; - let voters_value = dusk_value; - let generator_value = value - dusk_value - voters_value; - (dusk_value, generator_value, voters_value) + let one_tenth_reward = value / 10; + + let dusk_value = one_tenth_reward; + let voters_value = one_tenth_reward; + let generator_extra_value = one_tenth_reward; + + let generator_fixed_value = + value - dusk_value - voters_value - generator_extra_value; + + ( + dusk_value, + generator_fixed_value, + generator_extra_value, + voters_value, + ) } /// This implements the emission schedule described in the economic paper. diff --git a/rusk/src/lib/chain/rusk.rs b/rusk/src/lib/chain/rusk.rs index 2cc91f2ef7..2133f2dc21 100644 --- a/rusk/src/lib/chain/rusk.rs +++ b/rusk/src/lib/chain/rusk.rs @@ -542,14 +542,21 @@ fn reward_slash_and_update_root( slashing: &[StakePublicKey], voters: Option<&[(StakePublicKey, usize)]>, ) -> Result> { - let (dusk_value, generator_reward, voters_reward) = - coinbase_value(block_height, dusk_spent); - - if let Some(voters) = voters { - let credits: usize = voters.iter().map(|(_, credits)| credits).sum(); - if credits == 0 && block_height > 1 { - return Err(InvalidCreditsCount(block_height, 0)); - } + let ( + dusk_value, + generator_fixed_reward, + generator_extra_reward, + voters_reward, + ) = coinbase_value(block_height, dusk_spent); + + let credits = voters + .unwrap_or_default() + .iter() + .map(|(_, credits)| *credits as u64) + .sum::(); + + if voters.is_some() && credits == 0 && block_height > 1 { + return Err(InvalidCreditsCount(block_height, 0)); } let credit_reward = voters_reward / 64 * 2; @@ -569,6 +576,11 @@ fn reward_slash_and_update_root( reward = dusk_value ); + let reward_per_quota = generator_extra_reward / (21 * 2); + let generator_curr_extra_reward = + credits.saturating_sub(43 * 2) * reward_per_quota; + + let generator_reward = generator_fixed_reward + generator_curr_extra_reward; let r = session.call::<_, ()>( STAKE_CONTRACT, "reward", @@ -580,7 +592,9 @@ fn reward_slash_and_update_root( debug!( event = "generator rewarded", voter = to_bs58(generator), - reward = generator_reward + total_reward = generator_reward, + extra_reward = generator_curr_extra_reward, + credits, ); for (to_voter, credits) in voters.unwrap_or_default() { From a17235f2f58902eed8ff870cbb42ac060271a274 Mon Sep 17 00:00:00 2001 From: goshawk-3 Date: Fri, 5 Jul 2024 15:08:24 +0300 Subject: [PATCH 2/9] consensus: Exclude next generator from both ratification and validation committees of curr iter --- consensus/src/config.rs | 2 +- consensus/src/execution_ctx.rs | 6 ++--- consensus/src/phase.rs | 34 ++++++++++++++++++++++++--- consensus/src/quorum/verifiers.rs | 13 +++++++++- consensus/src/ratification/handler.rs | 4 ++-- consensus/src/user/committee.rs | 8 +++---- consensus/src/user/provisioners.rs | 15 ++++++------ consensus/src/user/sortition.rs | 10 ++++---- consensus/src/validation/handler.rs | 4 ++-- 9 files changed, 68 insertions(+), 28 deletions(-) diff --git a/consensus/src/config.rs b/consensus/src/config.rs index 75d6144f53..7ee947103c 100644 --- a/consensus/src/config.rs +++ b/consensus/src/config.rs @@ -28,4 +28,4 @@ pub const EMERGENCY_MODE_ITERATION_THRESHOLD: u8 = CONSENSUS_MAX_ITER - 50; pub const MIN_STEP_TIMEOUT: Duration = Duration::from_secs(7); pub const MAX_STEP_TIMEOUT: Duration = Duration::from_secs(40); pub const TIMEOUT_INCREASE: Duration = Duration::from_secs(2); -pub const MINIMUM_BLOCK_TIME: u64 = 10; +pub const MINIMUM_BLOCK_TIME: u64 = 2; diff --git a/consensus/src/execution_ctx.rs b/consensus/src/execution_ctx.rs index 35dc764cec..7b27bd4866 100644 --- a/consensus/src/execution_ctx.rs +++ b/consensus/src/execution_ctx.rs @@ -105,8 +105,8 @@ impl<'a, DB: Database, T: Operations + 'static> ExecutionCtx<'a, DB, T> { committee.is_member(&self.round_update.pubkey_bls) } - pub(crate) fn save_committee(&mut self, committee: Committee) { - self.iter_ctx.committees.insert(self.step(), committee); + pub(crate) fn save_committee(&mut self, step: u16, committee: Committee) { + self.iter_ctx.committees.insert(step, committee); } pub(crate) fn get_current_committee(&self) -> Option<&Committee> { @@ -435,7 +435,7 @@ impl<'a, DB: Database, T: Operations + 'static> ExecutionCtx<'a, DB, T> { pub fn get_sortition_config( &self, - exclusion: Option, + exclusion: Vec, ) -> sortition::Config { sortition::Config::new( self.round_update.seed(), diff --git a/consensus/src/phase.rs b/consensus/src/phase.rs index b98150c83a..ace7f48292 100644 --- a/consensus/src/phase.rs +++ b/consensus/src/phase.rs @@ -5,6 +5,7 @@ // Copyright (c) DUSK NETWORK. All rights reserved. use crate::commons::{ConsensusError, Database}; +use crate::config::CONSENSUS_MAX_ITER; use crate::execution_ctx::ExecutionCtx; use crate::operations::Operations; use crate::user::committee::Committee; @@ -62,13 +63,27 @@ impl Phase { debug!(event = "execute_step", ?timeout); let exclusion = match step_name { - StepName::Proposal => None, + StepName::Proposal => vec![], _ => { + let mut exclusion_list = vec![]; let generator = ctx .iter_ctx .get_generator(ctx.iteration) .expect("Proposal committee to be already generated"); - Some(generator) + + exclusion_list.push(generator); + + if ctx.iteration < CONSENSUS_MAX_ITER { + let next_generator = + ctx.iter_ctx.get_generator(ctx.iteration + 1).expect( + "Next Proposal committee to be already generated", + ); + + // TODO: Check for single-provisioner network setup + exclusion_list.push(next_generator); + } + + exclusion_list } }; @@ -82,12 +97,25 @@ impl Phase { &ctx.get_sortition_config(exclusion), ); + if let StepName::Proposal = step_name { + if ctx.iteration < CONSENSUS_MAX_ITER { + let mut cfg_next_iteration = ctx.get_sortition_config(vec![]); + cfg_next_iteration.step = + StepName::Proposal.to_step(ctx.iteration + 1); + + ctx.save_committee( + cfg_next_iteration.step, + Committee::new(ctx.provisioners, &cfg_next_iteration), + ); + } + } + debug!( event = "committee_generated", members = format!("{}", &step_committee) ); - ctx.save_committee(step_committee); + ctx.save_committee(ctx.step(), step_committee); // Execute step await_phase!(self, run(ctx)) diff --git a/consensus/src/quorum/verifiers.rs b/consensus/src/quorum/verifiers.rs index 9b6f22d77a..e003c5ce39 100644 --- a/consensus/src/quorum/verifiers.rs +++ b/consensus/src/quorum/verifiers.rs @@ -78,14 +78,25 @@ pub async fn verify_step_votes( let round = header.round; let iteration = header.iteration; + let mut exclusion_list = vec![]; let generator = committees_set .read() .await .provisioners() .get_generator(iteration, seed, round); + exclusion_list.push(generator); + + let next_generator = committees_set + .read() + .await + .provisioners() + .get_generator(iteration + 1, seed, round); + + exclusion_list.push(next_generator); + let cfg = - sortition::Config::new(seed, round, iteration, step, Some(generator)); + sortition::Config::new(seed, round, iteration, step, exclusion_list); if committees_set.read().await.get(&cfg).is_none() { let _ = committees_set.write().await.get_or_create(&cfg); diff --git a/consensus/src/ratification/handler.rs b/consensus/src/ratification/handler.rs index 4a5bce5d6b..cbbfc1d8bb 100644 --- a/consensus/src/ratification/handler.rs +++ b/consensus/src/ratification/handler.rs @@ -101,7 +101,7 @@ impl MsgHandler for RatificationHandler { sv, StepName::Ratification, quorum_reached, - committee.excluded().expect("Generator to be excluded"), + &committee.excluded()[0], // TODO ); if quorum_reached { @@ -144,7 +144,7 @@ impl MsgHandler for RatificationHandler { sv, StepName::Ratification, quorum_reached, - committee.excluded().expect("Generator to be excluded"), + &committee.excluded()[0], // TODO ) { return Ok(HandleMsgOutput::Ready(quorum_msg)); diff --git a/consensus/src/user/committee.rs b/consensus/src/user/committee.rs index 3066120d2b..efea578097 100644 --- a/consensus/src/user/committee.rs +++ b/consensus/src/user/committee.rs @@ -19,7 +19,7 @@ pub struct Committee { members: BTreeMap, super_majority: usize, majority: usize, - excluded: Option, + excluded: Vec, } impl Committee { @@ -50,7 +50,7 @@ impl Committee { members: BTreeMap::new(), super_majority, majority, - excluded: cfg.exclusion().copied(), + excluded: cfg.exclusion().clone(), }; for member_key in extracted { @@ -60,8 +60,8 @@ impl Committee { committee } - pub fn excluded(&self) -> Option<&PublicKeyBytes> { - self.excluded.as_ref() + pub fn excluded(&self) -> &Vec { + &self.excluded } /// Returns true if `pubkey_bls` is a member of the generated committee. diff --git a/consensus/src/user/provisioners.rs b/consensus/src/user/provisioners.rs index 47b53ea6f0..cd74eb0b4a 100644 --- a/consensus/src/user/provisioners.rs +++ b/consensus/src/user/provisioners.rs @@ -216,7 +216,7 @@ impl Provisioners { round, iteration, StepName::Proposal, - None, + vec![], ); let committee_keys = Committee::new(self, &cfg); @@ -238,17 +238,18 @@ impl<'a> CommitteeGenerator<'a> { fn from_provisioners( provisioners: &'a Provisioners, round: u64, - exclusion: Option<&PublicKeyBytes>, + exclusion: &Vec, ) -> Self { let eligibles = provisioners .eligibles(round) .map(|(p, stake)| (p, stake.clone())); - let members = match exclusion { - None => BTreeMap::from_iter(eligibles), - Some(excluded) => { - let eligibles = - eligibles.filter(|(p, _)| p.bytes() != excluded); + let members = match exclusion.len() { + 0 => BTreeMap::from_iter(eligibles), + _ => { + let eligibles = eligibles.filter(|(p, _)| { + !exclusion.iter().any(|excluded| excluded == p.bytes()) + }); BTreeMap::from_iter(eligibles) } }; diff --git a/consensus/src/user/sortition.rs b/consensus/src/user/sortition.rs index 3ffc7c6465..5041b1d94e 100644 --- a/consensus/src/user/sortition.rs +++ b/consensus/src/user/sortition.rs @@ -20,9 +20,9 @@ use crate::config::{ pub struct Config { seed: Seed, round: u64, - step: u16, + pub step: u16, committee_credits: usize, - exclusion: Option, + exclusion: Vec, } impl Config { @@ -31,7 +31,7 @@ impl Config { round: u64, iteration: u8, step: StepName, - exclusion: Option, + exclusion: Vec, ) -> Config { let committee_credits = match step { StepName::Proposal => PROPOSAL_COMMITTEE_CREDITS, @@ -60,8 +60,8 @@ impl Config { self.round } - pub fn exclusion(&self) -> Option<&PublicKeyBytes> { - self.exclusion.as_ref() + pub fn exclusion(&self) -> &Vec { + &self.exclusion } } diff --git a/consensus/src/validation/handler.rs b/consensus/src/validation/handler.rs index 9f00f03645..f7a9815eca 100644 --- a/consensus/src/validation/handler.rs +++ b/consensus/src/validation/handler.rs @@ -130,7 +130,7 @@ impl MsgHandler for ValidationHandler { sv, StepName::Validation, quorum_reached, - committee.excluded().expect("Generator to be excluded"), + &committee.excluded()[0], // TODO: ); if quorum_reached { @@ -182,7 +182,7 @@ impl MsgHandler for ValidationHandler { sv, StepName::Validation, quorum_reached, - committee.excluded().expect("Generator to be excluded"), + &committee.excluded()[0], // TODO: ) { return Ok(HandleMsgOutput::Ready(quorum_msg)); From 067858df3ede0a668373c12e7f8916aa1c863af2 Mon Sep 17 00:00:00 2001 From: goshawk-3 Date: Mon, 8 Jul 2024 11:17:24 +0300 Subject: [PATCH 3/9] consensus: Pass generator explicitly to any step message handler/collect --- consensus/src/aggregator.rs | 2 +- consensus/src/config.rs | 2 +- consensus/src/execution_ctx.rs | 14 ++++++++++++-- consensus/src/msg_handler.rs | 2 ++ consensus/src/phase.rs | 1 - consensus/src/proposal/handler.rs | 1 + consensus/src/proposal/step.rs | 3 ++- consensus/src/ratification/handler.rs | 4 +++- consensus/src/ratification/step.rs | 4 +++- consensus/src/user/sortition.rs | 18 +++++++++--------- consensus/src/validation/handler.rs | 4 +++- node/benches/accept.rs | 2 +- 12 files changed, 38 insertions(+), 19 deletions(-) diff --git a/consensus/src/aggregator.rs b/consensus/src/aggregator.rs index cbf21c61a7..b833ee2534 100644 --- a/consensus/src/aggregator.rs +++ b/consensus/src/aggregator.rs @@ -264,7 +264,7 @@ mod tests { } // Execute sortition with specific config - let cfg = Config::raw(Seed::from([4u8; 48]), round, 1, 10, None); + let cfg = Config::raw(Seed::from([4u8; 48]), round, 1, 10, vec![]); let c = Committee::new(&p, &cfg); let target_quorum = 7; diff --git a/consensus/src/config.rs b/consensus/src/config.rs index 7ee947103c..75d6144f53 100644 --- a/consensus/src/config.rs +++ b/consensus/src/config.rs @@ -28,4 +28,4 @@ pub const EMERGENCY_MODE_ITERATION_THRESHOLD: u8 = CONSENSUS_MAX_ITER - 50; pub const MIN_STEP_TIMEOUT: Duration = Duration::from_secs(7); pub const MAX_STEP_TIMEOUT: Duration = Duration::from_secs(40); pub const TIMEOUT_INCREASE: Duration = Duration::from_secs(2); -pub const MINIMUM_BLOCK_TIME: u64 = 2; +pub const MINIMUM_BLOCK_TIME: u64 = 10; diff --git a/consensus/src/execution_ctx.rs b/consensus/src/execution_ctx.rs index 7b27bd4866..5052789555 100644 --- a/consensus/src/execution_ctx.rs +++ b/consensus/src/execution_ctx.rs @@ -276,6 +276,9 @@ impl<'a, DB: Database, T: Operations + 'static> ExecutionCtx<'a, DB, T> { let committee = self .get_current_committee() .expect("committee to be created before run"); + + let generator = self.get_curr_generator(); + // Check if message is valid in the context of current step let valid = phase.lock().await.is_valid( &msg, @@ -335,7 +338,7 @@ impl<'a, DB: Database, T: Operations + 'static> ExecutionCtx<'a, DB, T> { let collected = phase .lock() .await - .collect(msg, &self.round_update, committee) + .collect(msg, &self.round_update, committee, generator) .await; match collected { @@ -380,6 +383,9 @@ impl<'a, DB: Database, T: Operations + 'static> ExecutionCtx<'a, DB, T> { let committee = self .get_current_committee() .expect("committee to be created before run"); + + let generator = self.get_curr_generator(); + if let Some(messages) = self .future_msgs .lock() @@ -421,7 +427,7 @@ impl<'a, DB: Database, T: Operations + 'static> ExecutionCtx<'a, DB, T> { if let Ok(HandleMsgOutput::Ready(msg)) = phase .lock() .await - .collect(msg, &self.round_update, committee) + .collect(msg, &self.round_update, committee, generator) .await { return Some(msg); @@ -463,4 +469,8 @@ impl<'a, DB: Database, T: Operations + 'static> ExecutionCtx<'a, DB, T> { ) .await; } + + pub(crate) fn get_curr_generator(&self) -> Option { + self.iter_ctx.get_generator(self.iteration) + } } diff --git a/consensus/src/msg_handler.rs b/consensus/src/msg_handler.rs index 852a1bc523..dec9517804 100644 --- a/consensus/src/msg_handler.rs +++ b/consensus/src/msg_handler.rs @@ -8,6 +8,7 @@ use crate::commons::{ConsensusError, RoundUpdate}; use crate::iteration_ctx::RoundCommittees; use crate::user::committee::Committee; use async_trait::async_trait; +use node_data::bls::PublicKeyBytes; use node_data::message::{Message, Status}; use node_data::StepName; use tracing::{debug, trace}; @@ -83,6 +84,7 @@ pub trait MsgHandler { msg: Message, ru: &RoundUpdate, committee: &Committee, + generator: Option, ) -> Result; /// collect allows each Phase to process a verified message from a former diff --git a/consensus/src/phase.rs b/consensus/src/phase.rs index ace7f48292..5ce672debf 100644 --- a/consensus/src/phase.rs +++ b/consensus/src/phase.rs @@ -79,7 +79,6 @@ impl Phase { "Next Proposal committee to be already generated", ); - // TODO: Check for single-provisioner network setup exclusion_list.push(next_generator); } diff --git a/consensus/src/proposal/handler.rs b/consensus/src/proposal/handler.rs index 13d8fdabfc..5990268118 100644 --- a/consensus/src/proposal/handler.rs +++ b/consensus/src/proposal/handler.rs @@ -44,6 +44,7 @@ impl MsgHandler for ProposalHandler { msg: Message, _ru: &RoundUpdate, _committee: &Committee, + _generator: Option, ) -> Result { // store candidate block let p = Self::unwrap_msg(&msg)?; diff --git a/consensus/src/proposal/step.rs b/consensus/src/proposal/step.rs index 0a43947147..dad73779e6 100644 --- a/consensus/src/proposal/step.rs +++ b/consensus/src/proposal/step.rs @@ -57,6 +57,7 @@ impl ProposalStep { let committee = ctx .get_current_committee() .expect("committee to be created before run"); + if ctx.am_member(committee) { let iteration = cmp::min(config::RELAX_ITERATION_THRESHOLD, ctx.iteration); @@ -84,7 +85,7 @@ impl ProposalStep { .handler .lock() .await - .collect(msg, &ctx.round_update, committee) + .collect(msg, &ctx.round_update, committee, None) .await { Ok(HandleMsgOutput::Ready(msg)) => return Ok(msg), diff --git a/consensus/src/ratification/handler.rs b/consensus/src/ratification/handler.rs index cbbfc1d8bb..569f3adf3f 100644 --- a/consensus/src/ratification/handler.rs +++ b/consensus/src/ratification/handler.rs @@ -8,6 +8,7 @@ use crate::commons::{ConsensusError, RoundUpdate}; use crate::msg_handler::{HandleMsgOutput, MsgHandler}; use crate::step_votes_reg::SafeAttestationInfoRegistry; use async_trait::async_trait; +use node_data::bls::PublicKeyBytes; use node_data::ledger::Attestation; use node_data::{ledger, StepName}; use tracing::{error, warn}; @@ -68,6 +69,7 @@ impl MsgHandler for RatificationHandler { msg: Message, ru: &RoundUpdate, committee: &Committee, + generator: Option, ) -> Result { let p = Self::unwrap_msg(msg)?; let iteration = p.header().iteration; @@ -101,7 +103,7 @@ impl MsgHandler for RatificationHandler { sv, StepName::Ratification, quorum_reached, - &committee.excluded()[0], // TODO + &generator.expect("There must be a valid generator"), ); if quorum_reached { diff --git a/consensus/src/ratification/step.rs b/consensus/src/ratification/step.rs index 52d0d297d3..22de96c132 100644 --- a/consensus/src/ratification/step.rs +++ b/consensus/src/ratification/step.rs @@ -122,6 +122,8 @@ impl RatificationStep { .get_current_committee() .expect("committee to be created before run"); + let generator = ctx.get_curr_generator(); + if ctx.am_member(committee) { let mut handler = self.handler.lock().await; let vote = handler.validation_result().vote(); @@ -137,7 +139,7 @@ impl RatificationStep { // Collect my own vote let res = handler - .collect(vote_msg, &ctx.round_update, committee) + .collect(vote_msg, &ctx.round_update, committee, generator) .await?; if let HandleMsgOutput::Ready(m) = res { return Ok(m); diff --git a/consensus/src/user/sortition.rs b/consensus/src/user/sortition.rs index 5041b1d94e..d60ef98e7f 100644 --- a/consensus/src/user/sortition.rs +++ b/consensus/src/user/sortition.rs @@ -110,7 +110,7 @@ mod tests { round: u64, step: u16, committee_credits: usize, - exclusion: Option, + exclusion: Vec, ) -> Config { Self { seed, @@ -132,7 +132,7 @@ mod tests { assert_eq!( create_sortition_hash( - &Config::raw(Seed::from([3; 48]), 10, 3, 0, None), + &Config::raw(Seed::from([3; 48]), 10, 3, 0, vec![]), 1 )[..], hash[..], @@ -148,7 +148,7 @@ mod tests { for (seed, total_weight, expected_score) in dataset { let hash = create_sortition_hash( - &Config::raw(Seed::from(seed), 10, 3, 0, None), + &Config::raw(Seed::from(seed), 10, 3, 0, vec![]), 1, ); @@ -166,7 +166,7 @@ mod tests { let committee_credits = 64; // Execute sortition with specific config - let cfg = Config::raw(Seed::default(), 1, 1, 64, None); + let cfg = Config::raw(Seed::default(), 1, 1, 64, vec![]); let committee = Committee::new(&p, &cfg); @@ -190,7 +190,7 @@ mod tests { 7777, 8, committee_credits, - None, + vec![], ); let committee = Committee::new(&p, &cfg); @@ -212,7 +212,7 @@ mod tests { let relative_step = 2; let step = iteration as u16 * 3 + relative_step; - let cfg = Config::raw(seed, round, step, committee_credits, None); + let cfg = Config::raw(seed, round, step, committee_credits, vec![]); let generator = p.get_generator(iteration, seed, round); let committee = Committee::new(&p, &cfg); @@ -228,7 +228,7 @@ mod tests { // Run the same extraction, with the generator excluded let cfg = - Config::raw(seed, round, step, committee_credits, Some(generator)); + Config::raw(seed, round, step, committee_credits, vec![generator]); let committee = Committee::new(&p, &cfg); assert!( @@ -249,7 +249,7 @@ mod tests { fn test_quorum() { let p = generate_provisioners(5); - let cfg = Config::raw(Seed::default(), 7777, 8, 64, None); + let cfg = Config::raw(Seed::default(), 7777, 8, 64, vec![]); let c = Committee::new(&p, &cfg); assert_eq!(c.super_majority_quorum(), 43); @@ -259,7 +259,7 @@ mod tests { fn test_intersect() { let p = generate_provisioners(10); - let cfg = Config::raw(Seed::default(), 1, 3, 200, None); + let cfg = Config::raw(Seed::default(), 1, 3, 200, vec![]); // println!("{:#?}", p); let c = Committee::new(&p, &cfg); diff --git a/consensus/src/validation/handler.rs b/consensus/src/validation/handler.rs index f7a9815eca..86148f6f70 100644 --- a/consensus/src/validation/handler.rs +++ b/consensus/src/validation/handler.rs @@ -9,6 +9,7 @@ use crate::commons::{ConsensusError, RoundUpdate}; use crate::msg_handler::{HandleMsgOutput, MsgHandler}; use crate::step_votes_reg::SafeAttestationInfoRegistry; use async_trait::async_trait; +use node_data::bls::PublicKeyBytes; use node_data::ledger::{Block, StepVotes}; use node_data::StepName; use tracing::{info, warn}; @@ -94,6 +95,7 @@ impl MsgHandler for ValidationHandler { msg: Message, _ru: &RoundUpdate, committee: &Committee, + generator: Option, ) -> Result { let p = Self::unwrap_msg(msg)?; @@ -130,7 +132,7 @@ impl MsgHandler for ValidationHandler { sv, StepName::Validation, quorum_reached, - &committee.excluded()[0], // TODO: + &generator.expect("There must be a valid generator"), ); if quorum_reached { diff --git a/node/benches/accept.rs b/node/benches/accept.rs index e8cf66fa51..3c4d46480a 100644 --- a/node/benches/accept.rs +++ b/node/benches/accept.rs @@ -44,7 +44,7 @@ fn create_step_votes( let generator = provisioners.get_generator(iteration, seed, round); let sortition_config = - SortitionConfig::new(seed, round, iteration, step, Some(generator)); + SortitionConfig::new(seed, round, iteration, step, vec![generator]); let committee = Committee::new(provisioners, &sortition_config); From 5247fddd4a793a9d35e9d4a10b929eaec8f1b9ef Mon Sep 17 00:00:00 2001 From: goshawk-3 Date: Mon, 8 Jul 2024 12:50:59 +0300 Subject: [PATCH 4/9] node-data: Remove both GetCandidate and GetCandidateResp messages --- node-data/src/message.rs | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/node-data/src/message.rs b/node-data/src/message.rs index 463ccb10b3..96900372b1 100644 --- a/node-data/src/message.rs +++ b/node-data/src/message.rs @@ -103,8 +103,6 @@ impl Serializable for Message { Payload::Quorum(p) => p.write(w), Payload::Block(p) => p.write(w), Payload::Transaction(p) => p.write(w), - Payload::GetCandidate(p) => p.write(w), - Payload::CandidateResp(p) => p.write(w), Payload::GetMempool(p) => p.write(w), Payload::GetInv(p) => p.write(w), Payload::GetBlocks(p) => p.write(w), @@ -135,12 +133,6 @@ impl Serializable for Message { Topics::Tx => { Message::new_transaction(ledger::Transaction::read(r)?) } - Topics::GetCandidateResp => Message::new_get_candidate_resp( - payload::GetCandidateResp::read(r)?, - ), - Topics::GetCandidate => { - Message::new_get_candidate(payload::GetCandidate::read(r)?) - } Topics::GetResource => { Message::new_get_resource(payload::GetResource::read(r)?) } @@ -213,24 +205,6 @@ impl Message { } } - /// Creates topics.GetCandidate message - pub fn new_get_candidate(p: payload::GetCandidate) -> Message { - Self { - topic: Topics::GetCandidate, - payload: Payload::GetCandidate(p), - ..Default::default() - } - } - - /// Creates topics.GetCandidateResp message - pub fn new_get_candidate_resp(p: payload::GetCandidateResp) -> Message { - Self { - topic: Topics::GetCandidateResp, - payload: Payload::CandidateResp(Box::new(p)), - ..Default::default() - } - } - /// Creates topics.Inv (inventory) message pub fn new_inv(p: payload::Inv) -> Message { Self { @@ -371,12 +345,10 @@ pub enum Payload { Block(Box), Transaction(Box), - GetCandidate(payload::GetCandidate), GetMempool(payload::GetMempool), GetInv(payload::Inv), GetBlocks(payload::GetBlocks), GetResource(payload::GetResource), - CandidateResp(Box), // Internal messages payload /// Result message passed from Validation step to Ratification step @@ -1087,14 +1059,12 @@ pub enum Topics { GetBlocks = 9, GetMempool = 13, // NB: This is aliased as Mempool in the golang impl GetInv = 14, // NB: This is aliased as Inv in the golang impl - GetCandidate = 46, // Fire-and-forget messaging Tx = 10, Block = 11, // Consensus main loop topics - GetCandidateResp = 15, Candidate = 16, Validation = 17, Ratification = 18, @@ -1126,8 +1096,6 @@ impl From for Topics { map_topic!(v, Topics::Block); map_topic!(v, Topics::GetMempool); map_topic!(v, Topics::GetInv); - map_topic!(v, Topics::GetCandidateResp); - map_topic!(v, Topics::GetCandidate); map_topic!(v, Topics::Candidate); map_topic!(v, Topics::Validation); map_topic!(v, Topics::Ratification); From e1be0ec815d77956040d7fb6383f4403da039892 Mon Sep 17 00:00:00 2001 From: goshawk-3 Date: Mon, 8 Jul 2024 12:51:25 +0300 Subject: [PATCH 5/9] node: Remove handler of GetCandidate msg --- node/src/databroker.rs | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/node/src/databroker.rs b/node/src/databroker.rs index 0c3334cafe..e6383f16ab 100644 --- a/node/src/databroker.rs +++ b/node/src/databroker.rs @@ -28,7 +28,6 @@ const TOPICS: &[u8] = &[ Topics::GetMempool as u8, Topics::GetInv as u8, Topics::GetResource as u8, - Topics::GetCandidate as u8, ]; struct Response { @@ -198,11 +197,6 @@ impl DataBrokerSrv { let this_peer = *network.read().await.public_addr(); match &msg.payload { - // Handle GetCandidate requests - Payload::GetCandidate(m) => { - let msg = Self::handle_get_candidate(db, m).await?; - Ok(Response::new_from_msg(msg, recv_peer)) - } // Handle GetBlocks requests Payload::GetBlocks(m) => { let msg = Self::handle_get_blocks(db, m, conf.max_inv_entries) @@ -257,29 +251,6 @@ impl DataBrokerSrv { } } - /// Handles GetCandidate requests. - /// - /// Message flow: GetCandidate -> CandidateResp - async fn handle_get_candidate( - db: &Arc>, - m: &payload::GetCandidate, - ) -> Result { - let res = db - .read() - .await - .view(|t| t.fetch_candidate_block(&m.hash)) - .map_err(|e| { - anyhow::anyhow!("could not fetch candidate block: {:?}", e) - })?; - - let block = - res.ok_or_else(|| anyhow::anyhow!("could not find candidate"))?; - - Ok(Message::new_get_candidate_resp(payload::GetCandidateResp { - candidate: block, - })) - } - /// Handles GetMempool requests. /// Message flow: GetMempool -> Inv -> GetResource -> Tx async fn handle_get_mempool( From 0779ebe3beed07568707f5cc3a573a9413a3a4d6 Mon Sep 17 00:00:00 2001 From: goshawk-3 Date: Mon, 8 Jul 2024 13:09:56 +0300 Subject: [PATCH 6/9] consensus: Pass generator to the handler of consensus messages from past iterations --- consensus/src/iteration_ctx.rs | 16 +++++++++++----- consensus/src/msg_handler.rs | 1 + consensus/src/proposal/handler.rs | 1 + consensus/src/quorum/verifiers.rs | 15 +++++++++------ consensus/src/ratification/handler.rs | 3 ++- consensus/src/validation/handler.rs | 3 ++- 6 files changed, 26 insertions(+), 13 deletions(-) diff --git a/consensus/src/iteration_ctx.rs b/consensus/src/iteration_ctx.rs index 08b8f4ff22..0d47a69f92 100644 --- a/consensus/src/iteration_ctx.rs +++ b/consensus/src/iteration_ctx.rs @@ -149,23 +149,29 @@ impl IterationCtx { msg: Message, ) -> Option { let committee = self.committees.get_committee(msg.get_step())?; + let generator = self.get_generator(msg.header.iteration); + match msg.topic() { node_data::message::Topics::Candidate => { let mut handler = self.proposal_handler.lock().await; - _ = handler.collect_from_past(msg, ru, committee).await; + _ = handler + .collect_from_past(msg, ru, committee, generator) + .await; } node_data::message::Topics::Validation => { let mut handler = self.validation_handler.lock().await; - if let Ok(HandleMsgOutput::Ready(m)) = - handler.collect_from_past(msg, ru, committee).await + if let Ok(HandleMsgOutput::Ready(m)) = handler + .collect_from_past(msg, ru, committee, generator) + .await { return Some(m); } } node_data::message::Topics::Ratification => { let mut handler = self.ratification_handler.lock().await; - if let Ok(HandleMsgOutput::Ready(m)) = - handler.collect_from_past(msg, ru, committee).await + if let Ok(HandleMsgOutput::Ready(m)) = handler + .collect_from_past(msg, ru, committee, generator) + .await { return Some(m); } diff --git a/consensus/src/msg_handler.rs b/consensus/src/msg_handler.rs index dec9517804..bfbfe22480 100644 --- a/consensus/src/msg_handler.rs +++ b/consensus/src/msg_handler.rs @@ -94,6 +94,7 @@ pub trait MsgHandler { msg: Message, ru: &RoundUpdate, committee: &Committee, + generator: Option, ) -> Result; /// handle_timeout allows each Phase to handle a timeout event. diff --git a/consensus/src/proposal/handler.rs b/consensus/src/proposal/handler.rs index 5990268118..abec8c450d 100644 --- a/consensus/src/proposal/handler.rs +++ b/consensus/src/proposal/handler.rs @@ -61,6 +61,7 @@ impl MsgHandler for ProposalHandler { _msg: Message, _ru: &RoundUpdate, _committee: &Committee, + _generator: Option, ) -> Result { Ok(HandleMsgOutput::Pending) } diff --git a/consensus/src/quorum/verifiers.rs b/consensus/src/quorum/verifiers.rs index e003c5ce39..df5ddf29bb 100644 --- a/consensus/src/quorum/verifiers.rs +++ b/consensus/src/quorum/verifiers.rs @@ -15,6 +15,7 @@ use crate::user::cluster::Cluster; use crate::user::committee::{Committee, CommitteeSet}; use crate::user::sortition; +use crate::config::CONSENSUS_MAX_ITER; use dusk_bytes::Serializable as BytesSerializable; use execution_core::{StakeAggPublicKey, StakeSignature}; use tokio::sync::RwLock; @@ -87,13 +88,15 @@ pub async fn verify_step_votes( exclusion_list.push(generator); - let next_generator = committees_set - .read() - .await - .provisioners() - .get_generator(iteration + 1, seed, round); + if iteration < CONSENSUS_MAX_ITER { + let next_generator = committees_set + .read() + .await + .provisioners() + .get_generator(iteration + 1, seed, round); - exclusion_list.push(next_generator); + exclusion_list.push(next_generator); + } let cfg = sortition::Config::new(seed, round, iteration, step, exclusion_list); diff --git a/consensus/src/ratification/handler.rs b/consensus/src/ratification/handler.rs index 569f3adf3f..bcc51ffaa6 100644 --- a/consensus/src/ratification/handler.rs +++ b/consensus/src/ratification/handler.rs @@ -125,6 +125,7 @@ impl MsgHandler for RatificationHandler { msg: Message, _ru: &RoundUpdate, committee: &Committee, + generator: Option, ) -> Result { let p = Self::unwrap_msg(msg)?; @@ -146,7 +147,7 @@ impl MsgHandler for RatificationHandler { sv, StepName::Ratification, quorum_reached, - &committee.excluded()[0], // TODO + &generator.expect("There must be a valid generator"), ) { return Ok(HandleMsgOutput::Ready(quorum_msg)); diff --git a/consensus/src/validation/handler.rs b/consensus/src/validation/handler.rs index 86148f6f70..e24dddc69e 100644 --- a/consensus/src/validation/handler.rs +++ b/consensus/src/validation/handler.rs @@ -159,6 +159,7 @@ impl MsgHandler for ValidationHandler { msg: Message, _ru: &RoundUpdate, committee: &Committee, + generator: Option, ) -> Result { let p = Self::unwrap_msg(msg)?; @@ -184,7 +185,7 @@ impl MsgHandler for ValidationHandler { sv, StepName::Validation, quorum_reached, - &committee.excluded()[0], // TODO: + &generator.expect("There must be a valid generator"), ) { return Ok(HandleMsgOutput::Ready(quorum_msg)); From 185c7f515e73dba4f7c55b37b531bb70380822b7 Mon Sep 17 00:00:00 2001 From: goshawk-3 Date: Mon, 8 Jul 2024 14:45:12 +0300 Subject: [PATCH 7/9] consensus: Provide additional default configs --- consensus/src/config.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/consensus/src/config.rs b/consensus/src/config.rs index 75d6144f53..ca89f99580 100644 --- a/consensus/src/config.rs +++ b/consensus/src/config.rs @@ -16,7 +16,12 @@ pub const MAJORITY_THRESHOLD: f64 = 0.5; /// Total credits of steps committees pub const PROPOSAL_COMMITTEE_CREDITS: usize = 1; pub const VALIDATION_COMMITTEE_CREDITS: usize = 64; +pub const VALIDATION_COMMITTEE_QUORUM: f64 = + VALIDATION_COMMITTEE_CREDITS as f64 * SUPERMAJORITY_THRESHOLD; + pub const RATIFICATION_COMMITTEE_CREDITS: usize = 64; +pub const RATIFICATION_COMMITTEE_QUORUM: f64 = + RATIFICATION_COMMITTEE_CREDITS as f64 * SUPERMAJORITY_THRESHOLD; pub const DEFAULT_BLOCK_GAS_LIMIT: u64 = 5 * 1_000_000_000; @@ -29,3 +34,34 @@ pub const MIN_STEP_TIMEOUT: Duration = Duration::from_secs(7); pub const MAX_STEP_TIMEOUT: Duration = Duration::from_secs(40); pub const TIMEOUT_INCREASE: Duration = Duration::from_secs(2); pub const MINIMUM_BLOCK_TIME: u64 = 10; + +/// Returns delta between full quorum and super_majority +pub fn validation_extra() -> usize { + VALIDATION_COMMITTEE_CREDITS - validation_committee_quorum() +} + +pub fn ratification_extra() -> usize { + RATIFICATION_COMMITTEE_CREDITS - ratification_committee_quorum() +} + +/// Returns ceil of RATIFICATION_COMMITTEE_QUORUM +pub fn ratification_committee_quorum() -> usize { + RATIFICATION_COMMITTEE_QUORUM.ceil() as usize +} + +/// Returns ceil of VALIDATION_COMMITTEE_QUORUM +pub fn validation_committee_quorum() -> usize { + VALIDATION_COMMITTEE_QUORUM.ceil() as usize +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_quorum_consts() { + assert_eq!(validation_committee_quorum(), 43); + assert_eq!(ratification_committee_quorum(), 43); + assert_eq!(validation_extra(), 21); + assert_eq!(ratification_extra(), 21); + } +} From 99f34311f9f4db2568585c95976f22e5d83554ab Mon Sep 17 00:00:00 2001 From: goshawk-3 Date: Mon, 8 Jul 2024 14:46:50 +0300 Subject: [PATCH 8/9] rusk: Use consensus configs to calc extra reward --- rusk/src/lib/chain.rs | 7 +++---- rusk/src/lib/chain/rusk.rs | 19 +++++++++++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/rusk/src/lib/chain.rs b/rusk/src/lib/chain.rs index 1a2e47fcb2..d778c39931 100644 --- a/rusk/src/lib/chain.rs +++ b/rusk/src/lib/chain.rs @@ -63,16 +63,15 @@ const fn coinbase_value( block_height: u64, dusk_spent: u64, ) -> (Dusk, Dusk, Dusk, Dusk) { - let value = emission_amount(block_height) + dusk_spent; - - let one_tenth_reward = value / 10; + let reward_value = emission_amount(block_height) + dusk_spent; + let one_tenth_reward = reward_value / 10; let dusk_value = one_tenth_reward; let voters_value = one_tenth_reward; let generator_extra_value = one_tenth_reward; let generator_fixed_value = - value - dusk_value - voters_value - generator_extra_value; + reward_value - dusk_value - voters_value - generator_extra_value; ( dusk_value, diff --git a/rusk/src/lib/chain/rusk.rs b/rusk/src/lib/chain/rusk.rs index 2133f2dc21..c51f3ee751 100644 --- a/rusk/src/lib/chain/rusk.rs +++ b/rusk/src/lib/chain/rusk.rs @@ -15,6 +15,10 @@ use tokio::task; use tracing::{debug, info, warn}; use dusk_bytes::{DeserializableSlice, Serializable}; +use dusk_consensus::config::{ + ratification_committee_quorum, ratification_extra, + validation_committee_quorum, validation_extra, +}; use dusk_consensus::operations::{ CallParams, VerificationOutput, VoterWithCredits, }; @@ -576,9 +580,8 @@ fn reward_slash_and_update_root( reward = dusk_value ); - let reward_per_quota = generator_extra_reward / (21 * 2); let generator_curr_extra_reward = - credits.saturating_sub(43 * 2) * reward_per_quota; + calc_generator_extra_reward(generator_extra_reward, credits); let generator_reward = generator_fixed_reward + generator_curr_extra_reward; let r = session.call::<_, ()>( @@ -636,6 +639,18 @@ fn reward_slash_and_update_root( Ok(events) } +/// Calculates current extra reward for Block generator. +fn calc_generator_extra_reward( + generator_extra_reward: Dusk, + credits: u64, +) -> u64 { + let reward_per_quota = generator_extra_reward + / (validation_extra() + ratification_extra()) as u64; + + let sum = ratification_committee_quorum() + validation_committee_quorum(); + credits.saturating_sub(sum as u64) * reward_per_quota +} + fn to_bs58(pk: &StakePublicKey) -> String { let mut pk = bs58::encode(&pk.to_bytes()).into_string(); pk.truncate(16); From 71b8cec2b30d221553059d7c05e5030c15bd0b66 Mon Sep 17 00:00:00 2001 From: goshawk-3 Date: Wed, 10 Jul 2024 13:20:05 +0300 Subject: [PATCH 9/9] rusk: Assign the entire generator_extra_reward if all 128 credits are collected --- rusk/src/lib/chain/rusk.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rusk/src/lib/chain/rusk.rs b/rusk/src/lib/chain/rusk.rs index c51f3ee751..53d1e3d989 100644 --- a/rusk/src/lib/chain/rusk.rs +++ b/rusk/src/lib/chain/rusk.rs @@ -18,6 +18,7 @@ use dusk_bytes::{DeserializableSlice, Serializable}; use dusk_consensus::config::{ ratification_committee_quorum, ratification_extra, validation_committee_quorum, validation_extra, + RATIFICATION_COMMITTEE_CREDITS, VALIDATION_COMMITTEE_CREDITS, }; use dusk_consensus::operations::{ CallParams, VerificationOutput, VoterWithCredits, @@ -644,6 +645,13 @@ fn calc_generator_extra_reward( generator_extra_reward: Dusk, credits: u64, ) -> u64 { + if credits + == (VALIDATION_COMMITTEE_CREDITS + RATIFICATION_COMMITTEE_CREDITS) + as u64 + { + return generator_extra_reward; + } + let reward_per_quota = generator_extra_reward / (validation_extra() + ratification_extra()) as u64;