diff --git a/consensus/src/commons.rs b/consensus/src/commons.rs index bb01718cc6..f62cf68ec2 100644 --- a/consensus/src/commons.rs +++ b/consensus/src/commons.rs @@ -20,7 +20,7 @@ use node_data::message::{AsyncQueue, Message, Payload}; use node_data::StepName; use tracing::error; -use crate::operations::VoterWithCredits; +use crate::operations::Voter; pub type TimeoutSet = HashMap; @@ -36,7 +36,7 @@ pub struct RoundUpdate { seed: Seed, hash: [u8; 32], att: Attestation, - att_voters: Vec, + att_voters: Vec, timestamp: u64, pub base_timeouts: TimeoutSet, @@ -48,7 +48,7 @@ impl RoundUpdate { secret_key: BlsSecretKey, tip_header: &Header, base_timeouts: TimeoutSet, - att_voters: Vec, + att_voters: Vec, ) -> Self { let round = tip_header.height + 1; RoundUpdate { @@ -80,7 +80,7 @@ impl RoundUpdate { self.timestamp } - pub fn att_voters(&self) -> &Vec { + pub fn att_voters(&self) -> &Vec { &self.att_voters } } diff --git a/consensus/src/operations.rs b/consensus/src/operations.rs index 41416a4c3c..3011102cfb 100644 --- a/consensus/src/operations.rs +++ b/consensus/src/operations.rs @@ -8,7 +8,7 @@ use std::fmt; use std::io; use std::time::Duration; -use execution_core::BlsPublicKey; +use node_data::bls::PublicKey; use node_data::ledger::{ Block, Fault, Header, InvalidFault, Slash, SpentTransaction, Transaction, }; @@ -17,7 +17,7 @@ use thiserror::Error; pub type StateRoot = [u8; 32]; pub type EventHash = [u8; 32]; -pub type VoterWithCredits = (BlsPublicKey, usize); +pub type Voter = (PublicKey, usize); #[derive(Debug, Error)] pub enum Error { @@ -53,7 +53,7 @@ pub struct CallParams { pub block_gas_limit: u64, pub generator_pubkey: node_data::bls::PublicKey, pub to_slash: Vec, - pub voters_pubkey: Option>, + pub voters_pubkey: Option>, } #[derive(Default)] @@ -86,7 +86,7 @@ pub trait Operations: Send + Sync { &self, candidate_header: &Header, disable_winning_att_check: bool, - ) -> Result<(u8, Vec, Vec), Error>; + ) -> Result<(u8, Vec, Vec), Error>; async fn verify_faults( &self, @@ -97,7 +97,7 @@ pub trait Operations: Send + Sync { async fn verify_state_transition( &self, blk: &Block, - voters: &[VoterWithCredits], + voters: &[Voter], ) -> Result; async fn execute_state_transition( diff --git a/consensus/src/proposal/block_generator.rs b/consensus/src/proposal/block_generator.rs index b70f4eba13..f6812c0afc 100644 --- a/consensus/src/proposal/block_generator.rs +++ b/consensus/src/proposal/block_generator.rs @@ -5,7 +5,7 @@ // Copyright (c) DUSK NETWORK. All rights reserved. use crate::commons::{get_current_timestamp, RoundUpdate}; -use crate::operations::{CallParams, Operations, VoterWithCredits}; +use crate::operations::{CallParams, Operations, Voter}; use node_data::ledger::{ to_str, Attestation, Block, Fault, IterationsInfo, Seed, Slash, }; @@ -90,7 +90,7 @@ impl Generator { iteration: u8, failed_iterations: IterationsInfo, faults: &[Fault], - voters: &[VoterWithCredits], + voters: &[Voter], ) -> Result { let to_slash = Slash::from_iterations_and_faults(&failed_iterations, faults)?; diff --git a/consensus/src/quorum/verifiers.rs b/consensus/src/quorum/verifiers.rs index e3db197598..2f3b0919de 100644 --- a/consensus/src/quorum/verifiers.rs +++ b/consensus/src/quorum/verifiers.rs @@ -6,11 +6,12 @@ use node_data::bls::PublicKey; use node_data::ledger::{Seed, StepVotes}; -use node_data::message::payload::{self, Quorum, Vote}; +use node_data::message::payload::{self, Vote}; use node_data::message::{ConsensusHeader, StepMessage}; use node_data::{Serializable, StepName}; use crate::commons::StepSigError; +use crate::operations::Voter; use crate::user::cluster::Cluster; use crate::user::committee::{Committee, CommitteeSet}; use crate::user::sortition; @@ -19,54 +20,6 @@ use crate::config::CONSENSUS_MAX_ITER; use dusk_bytes::Serializable as BytesSerializable; use execution_core::{BlsAggPublicKey, BlsSignature}; use tokio::sync::RwLock; -use tracing::error; - -/// Performs all three-steps verification of a quorum msg. -pub async fn verify_quorum( - quorum: &Quorum, - committees_set: &RwLock>, - seed: Seed, -) -> Result<(), StepSigError> { - // Verify validation - verify_step_votes( - &quorum.header, - quorum.vote(), - &quorum.att.validation, - committees_set, - seed, - StepName::Validation, - ) - .await - .map_err(|e| { - error!( - desc = "invalid validation", - sv = ?quorum.att.validation, - hdr = ?quorum.header, - ); - e - })?; - - // Verify ratification - verify_step_votes( - &quorum.header, - quorum.vote(), - &quorum.att.ratification, - committees_set, - seed, - StepName::Ratification, - ) - .await - .map_err(|e| { - error!( - desc = "invalid ratification", - sv = ?quorum.att.ratification, - hdr = ?quorum.header, - ); - e - })?; - - Ok(()) -} pub async fn verify_step_votes( header: &ConsensusHeader, @@ -75,7 +28,7 @@ pub async fn verify_step_votes( committees_set: &RwLock>, seed: Seed, step: StepName, -) -> Result<(QuorumResult, Committee), StepSigError> { +) -> Result<(QuorumResult, Vec), StepSigError> { let round = header.round; let iteration = header.iteration; @@ -108,8 +61,9 @@ pub async fn verify_step_votes( let set = committees_set.read().await; let committee = set.get(&cfg).expect("committee to be created"); - let quorum_result = verify_votes(header, step, vote, sv, committee)?; - Ok((quorum_result, committee.clone())) + let (quorum_result, voters) = + verify_votes(header, step, vote, sv, committee)?; + Ok((quorum_result, voters)) } #[derive(Default)] @@ -130,7 +84,7 @@ pub fn verify_votes( vote: &Vote, step_votes: &StepVotes, committee: &Committee, -) -> Result { +) -> Result<(QuorumResult, Vec), StepSigError> { let bitset = step_votes.bitset; let signature = step_votes.aggregate_signature().inner(); let sub_committee = committee.intersect(bitset); @@ -173,7 +127,7 @@ pub fn verify_votes( verify_step_signature(header, step, vote, apk, signature)?; } // Verification done - Ok(quorum_result) + Ok((quorum_result, sub_committee.to_voters())) } impl Cluster { @@ -190,6 +144,10 @@ impl Cluster { None => Err(StepSigError::EmptyApk), } } + + pub fn to_voters(self) -> Vec { + self.into_vec() + } } fn verify_step_signature( @@ -213,3 +171,64 @@ fn verify_step_signature( apk.verify(&sig, &msg)?; Ok(()) } + +pub async fn get_step_voters( + header: &ConsensusHeader, + sv: &StepVotes, + committees_set: &RwLock>, + seed: Seed, + step: StepName, +) -> Vec { + // compute committee for `step` + let committee = + get_step_committee(header, committees_set, seed, step).await; + + // extract quorum voters from `sv` + let bitset = sv.bitset; + let q_committee = committee.intersect(bitset); + + q_committee.to_voters() +} + +async fn get_step_committee( + header: &ConsensusHeader, + committees_set: &RwLock>, + seed: Seed, + step: StepName, +) -> Committee { + let round = header.round; + let iteration = header.iteration; + + // exclude current-iteration generator + let mut exclusion_list = vec![]; + let generator = committees_set + .read() + .await + .provisioners() + .get_generator(iteration, seed, round); + + exclusion_list.push(generator); + + // exclude next-iteration generator + if iteration < CONSENSUS_MAX_ITER { + 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, exclusion_list); + + if committees_set.read().await.get(&cfg).is_none() { + let _ = committees_set.write().await.get_or_create(&cfg); + } + + let set = committees_set.read().await; + let committee = set.get(&cfg).expect("committee to be created"); + + committee.clone() +} diff --git a/consensus/src/user/cluster.rs b/consensus/src/user/cluster.rs index 664f02f577..e91d2724bf 100644 --- a/consensus/src/user/cluster.rs +++ b/consensus/src/user/cluster.rs @@ -43,6 +43,10 @@ where self.0.iter() } + pub fn into_vec(self) -> Vec<(T, usize)> { + self.0.into_iter().collect() + } + pub fn contains_key(&self, key: &T) -> bool { self.0.contains_key(key) } diff --git a/consensus/src/validation/step.rs b/consensus/src/validation/step.rs index 2876ba7666..78ba6d9877 100644 --- a/consensus/src/validation/step.rs +++ b/consensus/src/validation/step.rs @@ -7,7 +7,7 @@ use crate::commons::{ConsensusError, Database, RoundUpdate}; use crate::config; use crate::execution_ctx::ExecutionCtx; -use crate::operations::{Operations, VoterWithCredits}; +use crate::operations::{Operations, Voter}; use crate::validation::handler; use anyhow::anyhow; use node_data::ledger::{to_str, Block}; @@ -140,7 +140,7 @@ impl ValidationStep { async fn call_vst( candidate: &Block, - voters: &[VoterWithCredits], + voters: &[Voter], executor: &Arc, ) -> anyhow::Result<()> { match executor.verify_state_transition(candidate, voters).await { diff --git a/node-data/src/ledger/header.rs b/node-data/src/ledger/header.rs index 1566e45348..3e663595d1 100644 --- a/node-data/src/ledger/header.rs +++ b/node-data/src/ledger/header.rs @@ -4,6 +4,8 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. +use crate::message::ConsensusHeader; + use super::*; pub type Seed = Signature; @@ -58,6 +60,15 @@ impl std::fmt::Debug for Header { } impl Header { + /// Return the corresponding ConsensusHeader + pub fn to_consensus_header(&self) -> ConsensusHeader { + ConsensusHeader { + prev_block_hash: self.prev_block_hash, + round: self.height, + iteration: self.iteration, + } + } + /// Marshal hashable fields. pub(crate) fn marshal_hashable( &self, diff --git a/node/benches/accept.rs b/node/benches/accept.rs index 10ba3a3c14..428c39df1e 100644 --- a/node/benches/accept.rs +++ b/node/benches/accept.rs @@ -26,6 +26,7 @@ use node_data::ledger::{Attestation, StepVotes}; use node_data::message::payload::{ QuorumType, RatificationResult, ValidationResult, Vote, }; +use node_data::message::ConsensusHeader; use node_data::{ledger, StepName}; use rand::rngs::StdRng; use rand::SeedableRng; @@ -105,8 +106,8 @@ where r } -pub fn verify_block_att(c: &mut Criterion) { - with_group("verify_block_att", c, |group| { +pub fn verify_att(c: &mut Criterion) { + with_group("verify_att", c, |group| { for input in INPUTS { group.measurement_time(Duration::from_secs(input.measurement_time)); let mut keys = vec![]; @@ -151,18 +152,25 @@ pub fn verify_block_att(c: &mut Criterion) { group.bench_function( BenchmarkId::new( - "verify_block_att", + "verify_att", format!("{} prov", input.provisioners), ), move |b| { b.to_async(FuturesExecutor).iter(|| async { - chain::verify_block_att( - [0u8; 32], + let consensus_header = ConsensusHeader { + prev_block_hash: [0u8; 32], + round: tip_header.height + 1, + iteration: 0, + }; + + chain::verify_att( + &att, + consensus_header, tip_header.seed, &provisioners, - tip_header.height + 1, - &att, - iteration, + RatificationResult::Success(Vote::Valid( + block_hash, + )), ) .await .expect("block to be verified") @@ -200,5 +208,5 @@ const INPUTS: &[Input] = &[ measurement_time: 15, }, ]; -criterion_group!(benches, verify_block_att); +criterion_group!(benches, verify_att); criterion_main!(benches); diff --git a/node/src/chain.rs b/node/src/chain.rs index 1ddf083f5d..e9ca1fb447 100644 --- a/node/src/chain.rs +++ b/node/src/chain.rs @@ -22,7 +22,7 @@ use crate::{LongLivedService, Message}; use anyhow::Result; use async_trait::async_trait; use dusk_consensus::commons::ConsensusError; -pub use header_validation::verify_block_att; +pub use header_validation::verify_att; use node_data::ledger::{to_str, BlockWithLabel, Label}; use node_data::message::AsyncQueue; use node_data::message::{Payload, Topics}; diff --git a/node/src/chain/acceptor.rs b/node/src/chain/acceptor.rs index 33f31052ff..2eca86ab68 100644 --- a/node/src/chain/acceptor.rs +++ b/node/src/chain/acceptor.rs @@ -17,7 +17,7 @@ use node_data::ledger::{ use node_data::message::AsyncQueue; use node_data::message::Payload; -use dusk_consensus::operations::VoterWithCredits; +use dusk_consensus::operations::Voter; use execution_core::stake::Withdraw; use metrics::{counter, gauge, histogram}; use node_data::message::payload::Vote; @@ -193,7 +193,7 @@ impl Acceptor { &self, provisioners_list: &Provisioners, tip: &Block, - ) -> Vec { + ) -> Vec { if tip.header().height == 0 { return vec![]; }; @@ -201,7 +201,6 @@ impl Acceptor { let prev_seed = self.get_prev_block_seed().await.expect("valid seed"); Validator::::get_voters(tip.header(), provisioners_list, prev_seed) .await - .expect("valid voters") } // Re-route message to consensus task @@ -1026,7 +1025,7 @@ pub(crate) async fn verify_block_header( prev_header: &ledger::Header, provisioners: &ContextProvisioners, header: &ledger::Header, -) -> anyhow::Result<(u8, Vec, Vec)> { +) -> anyhow::Result<(u8, Vec, Vec)> { let validator = Validator::new(db, prev_header, provisioners); validator.execute_checks(header, false).await } diff --git a/node/src/chain/consensus.rs b/node/src/chain/consensus.rs index a628526740..b4f43247ec 100644 --- a/node/src/chain/consensus.rs +++ b/node/src/chain/consensus.rs @@ -10,8 +10,7 @@ use async_trait::async_trait; use dusk_consensus::commons::{ConsensusError, RoundUpdate, TimeoutSet}; use dusk_consensus::consensus::Consensus; use dusk_consensus::operations::{ - self, CallParams, Error, Operations, Output, VerificationOutput, - VoterWithCredits, + self, CallParams, Error, Operations, Output, VerificationOutput, Voter, }; use dusk_consensus::queue::MsgRegistry; use dusk_consensus::user::provisioners::ContextProvisioners; @@ -87,7 +86,7 @@ impl Task { db: &Arc>, vm: &Arc>, base_timeout: TimeoutSet, - voters: Vec, + voters: Vec, ) { let current = provisioners_list.to_current(); let consensus_task = Consensus::new( @@ -248,7 +247,7 @@ impl Operations for Executor { &self, candidate_header: &Header, disable_winning_att_check: bool, - ) -> Result<(u8, Vec, Vec), Error> { + ) -> Result<(u8, Vec, Vec), Error> { let validator = Validator::new( self.db.clone(), &self.tip_header, @@ -277,7 +276,7 @@ impl Operations for Executor { async fn verify_state_transition( &self, blk: &Block, - voters: &[VoterWithCredits], + voters: &[Voter], ) -> Result { info!("verifying state"); diff --git a/node/src/chain/header_validation.rs b/node/src/chain/header_validation.rs index 2ab02d3834..afdb22f9e5 100644 --- a/node/src/chain/header_validation.rs +++ b/node/src/chain/header_validation.rs @@ -9,16 +9,18 @@ use crate::database::Ledger; use anyhow::anyhow; use dusk_bytes::Serializable; use dusk_consensus::config::MINIMUM_BLOCK_TIME; -use dusk_consensus::operations::VoterWithCredits; +use dusk_consensus::operations::Voter; use dusk_consensus::quorum::verifiers; use dusk_consensus::quorum::verifiers::QuorumResult; -use dusk_consensus::user::committee::{Committee, CommitteeSet}; +use dusk_consensus::user::committee::CommitteeSet; use dusk_consensus::user::provisioners::{ContextProvisioners, Provisioners}; use execution_core::stake::EPOCH; +use node_data::bls::PublicKey; use node_data::ledger::{to_str, Fault, InvalidFault, Seed, Signature}; use node_data::message::payload::{RatificationResult, Vote}; use node_data::message::ConsensusHeader; use node_data::{ledger, StepName}; +use std::collections::BTreeMap; use std::sync::Arc; use std::time::{SystemTime, UNIX_EPOCH}; use thiserror::Error; @@ -62,17 +64,23 @@ impl<'a, DB: database::DB> Validator<'a, DB> { pub async fn execute_checks( &self, candidate_block: &ledger::Header, - disable_winner_att_check: bool, - ) -> anyhow::Result<(u8, Vec, Vec)> - { + disable_att_check: bool, + ) -> anyhow::Result<(u8, Vec, Vec)> { self.verify_basic_fields(candidate_block).await?; + let prev_block_voters = self.verify_prev_block_cert(candidate_block).await?; let mut candidate_block_voters = vec![]; - if !disable_winner_att_check { - candidate_block_voters = - self.verify_success_att(candidate_block).await?; + if !disable_att_check { + (_, _, candidate_block_voters) = verify_att( + &candidate_block.att, + candidate_block.to_consensus_header(), + self.prev_header.seed, + self.provisioners.current(), + RatificationResult::Success(Vote::Valid(candidate_block.hash)), + ) + .await?; } let pni = self.verify_failed_iterations(candidate_block).await?; @@ -162,35 +170,25 @@ impl<'a, DB: database::DB> Validator<'a, DB> { pub async fn verify_prev_block_cert( &self, candidate_block: &'a ledger::Header, - ) -> anyhow::Result> { + ) -> anyhow::Result> { if self.prev_header.height == 0 { return Ok(vec![]); } + let prev_block_hash = candidate_block.prev_block_hash; + let prev_block_seed = self.db.read().await.view(|v| { v.fetch_block_header(&self.prev_header.prev_block_hash)? .ok_or_else(|| anyhow::anyhow!("Header not found")) .map(|h| h.seed) })?; - let cert_result = candidate_block.prev_block_cert.result; - let prev_block_hash = candidate_block.prev_block_hash; - - match candidate_block.prev_block_cert.result { - RatificationResult::Success(Vote::Valid(hash)) - if hash == prev_block_hash => {} - _ => anyhow::bail!( - "Invalid result for previous block hash: {cert_result:?}" - ), - } - - let (_, _, voters) = verify_block_att( - self.prev_header.prev_block_hash, + let (_, _, voters) = verify_att( + &candidate_block.prev_block_cert, + self.prev_header.to_consensus_header(), prev_block_seed, self.provisioners.prev(), - self.prev_header.height, - &candidate_block.prev_block_cert, - self.prev_header.iteration, + RatificationResult::Success(Vote::Valid(prev_block_hash)), ) .await?; @@ -216,10 +214,6 @@ impl<'a, DB: database::DB> Validator<'a, DB> { if let Some((att, pk)) = att { info!(event = "verify_att", att_type = "failed_att", iter); - if let RatificationResult::Success(_) = att.result { - anyhow::bail!("Failed iterations should not contains a RatificationResult::Success"); - } - let expected_pk = self.provisioners.current().get_generator( iter as u8, self.prev_header.seed, @@ -228,13 +222,12 @@ impl<'a, DB: database::DB> Validator<'a, DB> { anyhow::ensure!(pk == &expected_pk, "Invalid generator. Expected {expected_pk:?}, actual {pk:?}"); - let (_, rat_quorum, _) = verify_block_att( - self.prev_header.hash, + let (_, rat_quorum, _) = verify_att( + att, + candidate_block.to_consensus_header(), self.prev_header.seed, self.provisioners.current(), - candidate_block.height, - att, - iter as u8, + RatificationResult::Fail(Vote::default()), ) .await?; @@ -247,23 +240,6 @@ impl<'a, DB: database::DB> Validator<'a, DB> { Ok(candidate_block.iteration - failed_atts) } - pub async fn verify_success_att( - &self, - candidate_block: &'a ledger::Header, - ) -> anyhow::Result> { - let (_, _, voters) = verify_block_att( - self.prev_header.hash, - self.prev_header.seed, - self.provisioners.current(), - candidate_block.height, - &candidate_block.att, - candidate_block.iteration, - ) - .await?; - - Ok(voters) - } - /// Extracts voters list of a block. /// /// Returns a list of voters with their credits for both ratification and @@ -272,18 +248,31 @@ impl<'a, DB: database::DB> Validator<'a, DB> { blk: &'a ledger::Header, provisioners: &Provisioners, prev_block_seed: Seed, - ) -> anyhow::Result> { - let (_, _, voters) = verify_block_att( - blk.prev_block_hash, + ) -> Vec { + let att = &blk.att; + let consensus_header = blk.to_consensus_header(); + + let committee = RwLock::new(CommitteeSet::new(provisioners)); + + let validation_voters = verifiers::get_step_voters( + &consensus_header, + &att.validation, + &committee, prev_block_seed, - provisioners, - blk.height, - &blk.att, - blk.iteration, + StepName::Validation, ) - .await?; + .await; - Ok(voters) + let ratification_voters = verifiers::get_step_voters( + &consensus_header, + &att.ratification, + &committee, + prev_block_seed, + StepName::Ratification, + ) + .await; + + merge_voters(validation_voters, ratification_voters) } /// Verify faults inside a block. @@ -329,25 +318,43 @@ pub async fn verify_faults( Ok(()) } -pub async fn verify_block_att( - prev_block_hash: [u8; 32], +pub async fn verify_att( + att: &ledger::Attestation, + consensus_header: ConsensusHeader, curr_seed: Signature, curr_eligible_provisioners: &Provisioners, - round: u64, - att: &ledger::Attestation, - iteration: u8, -) -> anyhow::Result<(QuorumResult, QuorumResult, Vec)> { + expected_result: RatificationResult, +) -> anyhow::Result<(QuorumResult, QuorumResult, Vec)> { + // Check expected result + match (att.result, expected_result) { + // Both are Success and the inner Valid(Hash) values match + ( + RatificationResult::Success(Vote::Valid(r_hash)), + RatificationResult::Success(Vote::Valid(e_hash)), + ) => { + if r_hash != e_hash { + anyhow::bail!( + "Invalid Attestation: Expected block hash: {:?}, Got: {:?}", + e_hash, + r_hash + ) + } + } + // Both are Fail + (RatificationResult::Fail(_), RatificationResult::Fail(_)) => {} + // All other mismatches + _ => anyhow::bail!( + "Invalid Attestation: Result: {:?}, Expected: {:?}", + att.result, + expected_result + ), + } let committee = RwLock::new(CommitteeSet::new(curr_eligible_provisioners)); let mut result = (QuorumResult::default(), QuorumResult::default()); - let consensus_header = ConsensusHeader { - iteration, - round, - prev_block_hash, - }; - let v_committee; - let r_committee; + let validation_voters; + let ratification_voters; let vote = att.result.vote(); // Verify validation @@ -361,16 +368,16 @@ pub async fn verify_block_att( ) .await { - Ok((validation_quorum_result, committee)) => { + Ok((validation_quorum_result, voters)) => { result.0 = validation_quorum_result; - v_committee = committee; + validation_voters = voters; } Err(e) => { return Err(anyhow!( "invalid validation, vote = {:?}, round = {}, iter = {}, seed = {}, sv = {:?}, err = {}", vote, - round, - iteration, + consensus_header.round, + consensus_header.iteration, to_str(curr_seed.inner()), att.validation, e @@ -389,16 +396,16 @@ pub async fn verify_block_att( ) .await { - Ok((ratification_quorum_result, committee)) => { + Ok((ratification_quorum_result, voters)) => { result.1 = ratification_quorum_result; - r_committee = committee; + ratification_voters = voters; } Err(e) => { return Err(anyhow!( "invalid ratification, vote = {:?}, round = {}, iter = {}, seed = {}, sv = {:?}, err = {}", vote, - round, - iteration, + consensus_header.round, + consensus_header.iteration, to_str(curr_seed.inner()), att.ratification, e, @@ -406,21 +413,19 @@ pub async fn verify_block_att( } } - let voters = merge_committees(&v_committee, &r_committee); + let voters = merge_voters(validation_voters, ratification_voters); Ok((result.0, result.1, voters)) } -/// Merges two committees into a vector -fn merge_committees(a: &Committee, b: &Committee) -> Vec { - let mut members = a.members().clone(); - for (key, value) in b.members() { - // Keeps track of the number of occurrences for each member. - let counter = members.entry(key.clone()).or_insert(0); - *counter += *value; +/// Merges two Vec, summing up the usize values if the PublicKey is +/// repeated +fn merge_voters(v1: Vec, v2: Vec) -> Vec { + let mut voter_map: BTreeMap = BTreeMap::new(); + + for (pk, count) in v1.into_iter().chain(v2.into_iter()) { + let counter = voter_map.entry(pk).or_insert(0); + *counter += count; } - members - .into_iter() - .map(|(key, credits)| (*key.inner(), credits)) - .collect() + voter_map.into_iter().collect() } diff --git a/node/src/vm.rs b/node/src/vm.rs index 91dd4f3010..92fa840259 100644 --- a/node/src/vm.rs +++ b/node/src/vm.rs @@ -4,7 +4,7 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -use dusk_consensus::operations::VoterWithCredits; +use dusk_consensus::operations::Voter; use dusk_consensus::{ operations::{CallParams, VerificationOutput}, user::{provisioners::Provisioners, stake::Stake}, @@ -29,13 +29,13 @@ pub trait VMExecution: Send + Sync + 'static { fn verify_state_transition( &self, blk: &Block, - voters: Option<&[VoterWithCredits]>, + voters: Option<&[Voter]>, ) -> anyhow::Result; fn accept( &self, blk: &Block, - voters: Option<&[VoterWithCredits]>, + voters: Option<&[Voter]>, ) -> anyhow::Result<(Vec, VerificationOutput)>; fn finalize_state( diff --git a/rusk/src/lib/chain/rusk.rs b/rusk/src/lib/chain/rusk.rs index bf6a7b68da..440736bad9 100644 --- a/rusk/src/lib/chain/rusk.rs +++ b/rusk/src/lib/chain/rusk.rs @@ -20,9 +20,7 @@ use dusk_consensus::config::{ validation_committee_quorum, validation_extra, RATIFICATION_COMMITTEE_CREDITS, VALIDATION_COMMITTEE_CREDITS, }; -use dusk_consensus::operations::{ - CallParams, VerificationOutput, VoterWithCredits, -}; +use dusk_consensus::operations::{CallParams, VerificationOutput, Voter}; use execution_core::bytecode::Bytecode; use execution_core::transfer::ContractDeploy; use execution_core::{ @@ -220,7 +218,7 @@ impl Rusk { generator: &BlsPublicKey, txs: &[Transaction], slashing: Vec, - voters: Option<&[VoterWithCredits]>, + voters: Option<&[Voter]>, ) -> Result<(Vec, VerificationOutput)> { let session = self.session(block_height, None)?; @@ -251,7 +249,7 @@ impl Rusk { txs: Vec, consistency_check: Option, slashing: Vec, - voters: Option<&[VoterWithCredits]>, + voters: Option<&[Voter]>, ) -> Result<(Vec, VerificationOutput)> { let session = self.session(block_height, None)?; @@ -440,7 +438,7 @@ fn accept( generator: &BlsPublicKey, txs: &[Transaction], slashing: Vec, - voters: Option<&[VoterWithCredits]>, + voters: Option<&[Voter]>, gas_per_deploy_byte: Option, ) -> Result<( Vec, @@ -670,7 +668,7 @@ fn reward_slash_and_update_root( dusk_spent: Dusk, generator: &BlsPublicKey, slashing: Vec, - voters: Option<&[(BlsPublicKey, usize)]>, + voters: Option<&[Voter]>, ) -> Result> { let ( dusk_value, @@ -689,8 +687,6 @@ fn reward_slash_and_update_root( return Err(InvalidCreditsCount(block_height, 0)); } - let credit_reward = voters_reward / 64 * 2; - let r = session.call::<_, ()>( STAKE_CONTRACT, "reward", @@ -720,25 +716,30 @@ fn reward_slash_and_update_root( debug!( event = "generator rewarded", - voter = to_bs58(generator), + generator = to_bs58(generator), total_reward = generator_reward, extra_reward = generator_curr_extra_reward, credits, ); + let credit_reward = voters_reward + / (VALIDATION_COMMITTEE_CREDITS + RATIFICATION_COMMITTEE_CREDITS) + as u64; + for (to_voter, credits) in voters.unwrap_or_default() { + let voter = to_voter.inner(); let voter_reward = *credits as u64 * credit_reward; let r = session.call::<_, ()>( STAKE_CONTRACT, "reward", - &(*to_voter, voter_reward), + &(*voter, voter_reward), u64::MAX, )?; events.extend(r.events); debug!( event = "validator of prev block rewarded", - voter = to_bs58(to_voter), + voter = to_bs58(voter), credits = *credits, reward = voter_reward ) diff --git a/rusk/src/lib/chain/vm.rs b/rusk/src/lib/chain/vm.rs index aa15ca1f7f..9714ca0cdf 100644 --- a/rusk/src/lib/chain/vm.rs +++ b/rusk/src/lib/chain/vm.rs @@ -9,9 +9,7 @@ mod query; use tracing::info; use dusk_bytes::DeserializableSlice; -use dusk_consensus::operations::{ - CallParams, VerificationOutput, VoterWithCredits, -}; +use dusk_consensus::operations::{CallParams, VerificationOutput, Voter}; use dusk_consensus::user::provisioners::Provisioners; use dusk_consensus::user::stake::Stake; use execution_core::{ @@ -46,7 +44,7 @@ impl VMExecution for Rusk { fn verify_state_transition( &self, blk: &Block, - voters: Option<&[VoterWithCredits]>, + voters: Option<&[Voter]>, ) -> anyhow::Result { info!("Received verify_state_transition request"); let generator = blk.header().generator_bls_pubkey; @@ -72,7 +70,7 @@ impl VMExecution for Rusk { fn accept( &self, blk: &Block, - voters: Option<&[VoterWithCredits]>, + voters: Option<&[Voter]>, ) -> anyhow::Result<(Vec, VerificationOutput)> { info!("Received accept request"); let generator = blk.header().generator_bls_pubkey;