diff --git a/consensus/example/consensus_service.rs b/consensus/example/consensus_service.rs index 1047b5b1b4..08f87d6883 100644 --- a/consensus/example/consensus_service.rs +++ b/consensus/example/consensus_service.rs @@ -53,7 +53,7 @@ pub fn run_main_loop( // Load provisioners keys from external consensus keys. // The loaded keys should be the same as the ones from Genesis State. let keys = load_provisioners_keys(provisioners_num); - let mut provisioners = Provisioners::new(); + let mut provisioners = Provisioners::default(); for (_, (_, pk)) in keys.iter().enumerate() { provisioners.add_member_with_value(pk.clone(), 1000 * DUSK * 10); diff --git a/consensus/example/main.rs b/consensus/example/main.rs index cd6b6fccf3..3f7127f85d 100644 --- a/consensus/example/main.rs +++ b/consensus/example/main.rs @@ -43,7 +43,7 @@ fn generate_keys(n: u64) -> Vec<(SecretKey, PublicKey)> { fn generate_provisioners_from_keys( keys: Vec<(SecretKey, PublicKey)>, ) -> Provisioners { - let mut p = Provisioners::new(); + let mut p = Provisioners::default(); for (pos, (_, pk)) in keys.into_iter().enumerate() { p.add_member_with_value(pk, 1000 * (pos as u64) * DUSK); diff --git a/consensus/src/aggregator.rs b/consensus/src/aggregator.rs index 7d7e32989e..a11663fbc3 100644 --- a/consensus/src/aggregator.rs +++ b/consensus/src/aggregator.rs @@ -225,7 +225,7 @@ mod tests { // Create provisioners // Also populate a vector of headers - let mut p = Provisioners::new(); + let mut p = Provisioners::default(); let mut input = vec![]; for sk in sks { @@ -248,14 +248,12 @@ mod tests { input.push((signature, header)); } - p.update_eligibility_flag(round); - // Execute sortition with specific config let cfg = Config::new(Seed::from([4u8; 48]), round, step, 10); let c = Committee::new( node_data::bls::PublicKey::new(PublicKey::default()), - &mut p, - cfg, + &p, + &cfg, ); let target_quorum = 7; diff --git a/consensus/src/commons.rs b/consensus/src/commons.rs index 7388f5f4ee..69629c685c 100644 --- a/consensus/src/commons.rs +++ b/consensus/src/commons.rs @@ -27,7 +27,6 @@ use tokio::task::JoinSet; use tracing::{debug, error}; #[derive(Clone, Default, Debug)] -#[allow(unused)] pub struct RoundUpdate { // Current round number of the ongoing consensus pub round: u64, diff --git a/consensus/src/consensus.rs b/consensus/src/consensus.rs index c7ae65f493..1854e5fc8d 100644 --- a/consensus/src/consensus.rs +++ b/consensus/src/consensus.rs @@ -99,13 +99,10 @@ impl Consensus { pub async fn spin( &mut self, ru: RoundUpdate, - mut provisioners: Provisioners, + provisioners: Provisioners, cancel_rx: oneshot::Receiver, ) -> Result { let round = ru.round; - // Enable/Disable all members stakes depending on the current round. If - // a stake is not eligible for this round, it's disabled. - provisioners.update_eligibility_flag(round); // Agreement loop Executes agreement loop in a separate tokio::task to // collect (aggr)Agreement messages. diff --git a/consensus/src/firststep/step.rs b/consensus/src/firststep/step.rs index e58ef4fdf8..83365549e1 100644 --- a/consensus/src/firststep/step.rs +++ b/consensus/src/firststep/step.rs @@ -18,7 +18,6 @@ use std::sync::Arc; use tokio::sync::Mutex; use tracing::debug; -#[allow(unused)] pub struct Reduction { timeout_millis: u64, handler: Arc>>, diff --git a/consensus/src/phase.rs b/consensus/src/phase.rs index 0d8cec4959..aef2c0e69c 100644 --- a/consensus/src/phase.rs +++ b/consensus/src/phase.rs @@ -69,7 +69,7 @@ impl Phase { let step_committee = Committee::new( ctx.round_update.pubkey_bls.clone(), ctx.provisioners, - ctx.get_sortition_config(size), + &ctx.get_sortition_config(size), ); debug!( diff --git a/consensus/src/secondstep/step.rs b/consensus/src/secondstep/step.rs index af311c1b48..ec4e9a0543 100644 --- a/consensus/src/secondstep/step.rs +++ b/consensus/src/secondstep/step.rs @@ -17,7 +17,6 @@ use node_data::message::{Message, Payload, Topics}; use std::sync::Arc; use tokio::sync::Mutex; -#[allow(unused)] pub struct Reduction { handler: Arc>, candidate: Option, diff --git a/consensus/src/user/committee.rs b/consensus/src/user/committee.rs index 0693ac807c..55a7240169 100644 --- a/consensus/src/user/committee.rs +++ b/consensus/src/user/committee.rs @@ -14,12 +14,12 @@ use std::collections::{BTreeMap, HashMap, HashSet}; use std::fmt; use std::mem; -#[allow(unused)] #[derive(Default, Debug, Clone)] pub struct Committee { members: BTreeMap, this_member_key: PublicKey, - cfg: sortition::Config, + quorum: usize, + nil_quorum: usize, } impl Committee { @@ -28,7 +28,6 @@ impl Committee { } } -#[allow(unused)] impl Committee { /// Generates a new committee from the given provisioners state and /// sortition config. @@ -41,19 +40,24 @@ impl Committee { /// method. pub fn new( pubkey_bls: PublicKey, - provisioners: &mut Provisioners, - cfg: sortition::Config, + provisioners: &Provisioners, + cfg: &sortition::Config, ) -> Self { - provisioners.update_eligibility_flag(cfg.round); // Generate committee using deterministic sortition. - let res = provisioners.create_committee(&cfg); + let res = provisioners.create_committee(cfg); + + let quorum = (cfg.committee_size as f64 + * config::CONSENSUS_QUORUM_THRESHOLD) + .ceil() as usize; + let nil_quorum = quorum; // Turn the raw vector into a hashmap where we map a pubkey to its // occurrences. let mut committee = Self { members: BTreeMap::new(), this_member_key: pubkey_bls, - cfg, + nil_quorum, + quorum, }; for member_key in res { @@ -94,14 +98,12 @@ impl Committee { /// Returns target quorum for the generated committee. pub fn quorum(&self) -> usize { - (self.cfg.committee_size as f64 * config::CONSENSUS_QUORUM_THRESHOLD) - .ceil() as usize + self.quorum } /// Returns target NIL quorum for the generated committee. pub fn nil_quorum(&self) -> usize { - (self.cfg.committee_size as f64 * config::CONSENSUS_NILQUORUM_THRESHOLD) - .ceil() as usize + self.nil_quorum } pub fn bits(&self, voters: &Cluster) -> u64 { @@ -109,7 +111,6 @@ impl Committee { debug_assert!(self.members.len() <= mem::size_of_val(&bits) * 8); - let mut pos = 0; for (pk, _) in voters.iter() { for (pos, (member_pk, _)) in self.members.iter().enumerate() { if member_pk.eq(pk) { @@ -251,8 +252,8 @@ impl CommitteeSet { .or_insert_with_key(|config| { Committee::new( self.this_member_key.clone(), - &mut self.provisioners, - config.clone(), + &self.provisioners, + config, ) }) } diff --git a/consensus/src/user/provisioners.rs b/consensus/src/user/provisioners.rs index 8769831b9a..c005e1d794 100644 --- a/consensus/src/user/provisioners.rs +++ b/consensus/src/user/provisioners.rs @@ -13,91 +13,12 @@ use std::collections::BTreeMap; pub const DUSK: u64 = 1_000_000_000; -#[derive(Clone, Debug)] -#[allow(unused)] -pub struct Member { - /// Vector of pairs (stake and eligibility flag) - stakes: Vec<(Stake, bool)>, - pubkey_bls: PublicKey, -} - -impl Member { - pub fn new(pubkey_bls: PublicKey) -> Self { - Self { - stakes: vec![], - pubkey_bls, - } - } - - pub fn first_stake(&self) -> Option<&Stake> { - self.stakes.first().map(|(s, _)| s) - } - - pub fn public_key(&self) -> &PublicKey { - &self.pubkey_bls - } - - // AddStake appends a stake to the stake set with eligible_flag=false. - pub fn add_stake(&mut self, stake: Stake) { - self.stakes.push((stake, false)); - } - - pub fn update_eligibility_flag(&mut self, round: u64) { - for (stake, eligible) in self.stakes.iter_mut() { - *eligible = stake.eligible_since <= round; - } - } - - pub fn is_eligible(&self, round: u64) -> bool { - self.stakes.iter().any(|(s, _)| s.eligible_since <= round) - } - - pub fn subtract_from_stake(&mut self, value: u64) -> u64 { - for (stake, _) in self.stakes.iter_mut() { - let stake_val = stake.intermediate_value; - if stake_val > 0 { - if stake_val < value { - stake.intermediate_value = 0; - return stake_val; - } - stake.intermediate_value -= value; - return value; - } - } - - 0 - } - - pub fn restore_intermediate_value(&mut self) { - for stake in self.stakes.iter_mut() { - stake.0.restore_intermediate_value(); - } - } - - fn get_total_eligible_stake(&self) -> BigInt { - let mut total: u64 = 0; - for (stake, eligible) in self.stakes.iter() { - if *eligible { - total += stake.intermediate_value; - } - } - - BigInt::from(total) - } -} - #[derive(Clone, Default, Debug)] pub struct Provisioners { - members: BTreeMap, + members: BTreeMap, } impl Provisioners { - pub fn new() -> Self { - Self { - members: BTreeMap::new(), - } - } - /// Adds a provisioner with stake. /// /// It appends the stake if the given provisioner already exists. @@ -106,24 +27,14 @@ impl Provisioners { pubkey_bls: PublicKey, stake: Stake, ) { - self.members - .entry(pubkey_bls) - .or_insert_with_key(|key| Member::new(key.clone())) - .add_stake(stake); + self.members.entry(pubkey_bls).or_insert_with(|| stake); } /// Adds a new member with reward=0 and elibile_since=0. /// /// Useful for implementing unit tests. pub fn add_member_with_value(&mut self, pubkey_bls: PublicKey, value: u64) { - self.add_member_with_stake(pubkey_bls, Stake::new(value, 0, 0)); - } - - /// Turns on/off elibility flag of stakes for a given round. - pub fn update_eligibility_flag(&mut self, round: u64) { - self.members - .values_mut() - .for_each(|m| m.update_eligibility_flag(round)); + self.add_member_with_stake(pubkey_bls, Stake::from_value(value)); } // Returns a pair of count of all provisioners and count of eligible @@ -138,28 +49,20 @@ impl Provisioners { (self.members.len(), eligible_len) } - /// Returns a member of Provisioner list by public key. - pub fn get_member(&self, key: &PublicKey) -> Option<&Member> { - self.members.get(key) - } - /// Runs the deterministic sortition algorithm which determines the /// committee members for a given round, step and seed. /// /// Returns a vector of provisioners public keys. pub(crate) fn create_committee( - &mut self, + &self, cfg: &sortition::Config, ) -> Vec { let mut committee: Vec = vec![]; - // Restore intermediate value of all stakes. - for (_, member) in self.members.iter_mut() { - member.restore_intermediate_value(); - } + let mut comm = CommitteeGenerator::from_provisioners(self, cfg.round); let mut total_amount_stake = - BigInt::from(self.calc_total_eligible_weight()); + BigInt::from(comm.calc_total_eligible_weight()); let mut counter: u32 = 0; loop { @@ -178,10 +81,10 @@ impl Provisioners { sortition::generate_sortition_score(hash, &total_amount_stake); // NB: The public key can be extracted multiple times per committee. - match self.extract_and_subtract_member(score) { + match comm.extract_and_subtract_member(score) { Some((pk, value)) => { // append the public key to the committee set. - committee.push(pk.clone()); + committee.push(pk); let subtracted_stake = value; if total_amount_stake > subtracted_stake { @@ -196,16 +99,26 @@ impl Provisioners { committee } +} + +#[derive(Default)] +struct CommitteeGenerator<'a> { + members: BTreeMap<&'a PublicKey, Stake>, +} + +impl<'a> CommitteeGenerator<'a> { + fn from_provisioners(provisioners: &'a Provisioners, round: u64) -> Self { + let provs = provisioners.members.iter().filter_map(|(p, stake)| { + stake.is_eligible(round).then_some((p, stake.clone())) + }); + Self { + members: BTreeMap::from_iter(provs), + } + } /// Sums up the total weight of all **eligible** stakes fn calc_total_eligible_weight(&self) -> u64 { - self.members - .values() - .flat_map(|m| &m.stakes) - .filter_map(|(stake, eligible)| { - eligible.then(|| stake.intermediate_value) - }) - .sum() + self.members.values().map(|m| m.value()).sum() } fn extract_and_subtract_member( @@ -217,18 +130,14 @@ impl Provisioners { } loop { - for member in self.members.values_mut() { - let total_stake = member.get_total_eligible_stake(); + for (&pk, stake) in self.members.iter_mut() { + let total_stake = BigInt::from(stake.value()); if total_stake >= score { // Subtract 1 DUSK from the value extracted and rebalance // accordingly. - let subtracted_stake = - BigInt::from(member.subtract_from_stake(DUSK)); + let subtracted_stake = BigInt::from(stake.subtract(DUSK)); - return Some(( - member.public_key().clone(), - subtracted_stake, - )); + return Some((pk.clone(), subtracted_stake)); } score -= total_stake; diff --git a/consensus/src/user/stake.rs b/consensus/src/user/stake.rs index f29537e036..da1691c3ed 100644 --- a/consensus/src/user/stake.rs +++ b/consensus/src/user/stake.rs @@ -5,17 +5,9 @@ // Copyright (c) DUSK NETWORK. All rights reserved. #[derive(Clone, Default, Debug)] -#[allow(unused)] pub struct Stake { - // Value should be initialized only at constructor. - // It's later used to restore intermediate_value on each new sortition - // execution. In that way, we don't need to perform a deep copy of all - // provisioners members and their stakes as it used to be. value: u64, - // TODO: Move intermediate_value to member struct so that we keep stake - // definition lean. - pub intermediate_value: u64, pub reward: u64, pub counter: u64, pub eligible_since: u64, @@ -25,14 +17,39 @@ impl Stake { pub fn new(value: u64, reward: u64, eligible_since: u64) -> Self { Self { value, - intermediate_value: value, reward, eligible_since, counter: 0, } } - pub fn restore_intermediate_value(&mut self) { - self.intermediate_value = self.value; + pub fn value(&self) -> u64 { + self.value + } + + pub fn from_value(value: u64) -> Self { + Self { + value, + ..Default::default() + } + } + + pub fn is_eligible(&self, round: u64) -> bool { + self.eligible_since <= round + } + + /// Subtract `sub` from the stake without overflowing if the stake is not + /// enough. + /// + /// Return the effective subtracted amount + pub fn subtract(&mut self, sub: u64) -> u64 { + if self.value <= sub { + let sub = self.value; + self.value = 0; + sub + } else { + self.value -= sub; + sub + } } } diff --git a/consensus/tests/sortition.rs b/consensus/tests/sortition.rs index 1125c200fe..f426d755c4 100644 --- a/consensus/tests/sortition.rs +++ b/consensus/tests/sortition.rs @@ -17,15 +17,14 @@ use node_data::ledger::Seed; #[test] fn test_deterministic_sortition_1() { // Create provisioners with bls keys read from an external file. - let mut p = generate_provisioners(5); + let p = generate_provisioners(5); let committee_size = 64; // Execute sortition with specific config let cfg = Config::new(Seed::default(), 1, 1, 64); - p.update_eligibility_flag(cfg.round); - let committee = Committee::new(PublicKey::default(), &mut p, cfg); + let committee = Committee::new(PublicKey::default(), &p, &cfg); // Verify expected committee size assert_eq!( @@ -40,12 +39,12 @@ fn test_deterministic_sortition_1() { #[test] fn test_deterministic_sortition_2() { // Create provisioners with bls keys read from an external file. - let mut p = generate_provisioners(5); + let p = generate_provisioners(5); let committee_size = 45; let cfg = Config::new(Seed::from([3u8; 48]), 7777, 8, committee_size); - let committee = Committee::new(PublicKey::default(), &mut p, cfg); + let committee = Committee::new(PublicKey::default(), &p, &cfg); assert_eq!( committee_size, committee.get_occurrences().iter().sum::() @@ -56,24 +55,22 @@ fn test_deterministic_sortition_2() { #[test] fn test_quorum() { // Create provisioners with bls keys read from an external file. - let mut p = generate_provisioners(5); + let p = generate_provisioners(5); let cfg = Config::new(Seed::default(), 7777, 8, 64); - p.update_eligibility_flag(cfg.round); - let c = Committee::new(PublicKey::default(), &mut p, cfg); + let c = Committee::new(PublicKey::default(), &p, &cfg); assert_eq!(c.quorum(), 43); } #[test] fn test_intersect() { - let mut p = generate_provisioners(10); + let p = generate_provisioners(10); let cfg = Config::new(Seed::default(), 1, 3, 200); - p.update_eligibility_flag(cfg.round); // println!("{:#?}", p); - let c = Committee::new(PublicKey::default(), &mut p, cfg); + let c = Committee::new(PublicKey::default(), &p, &cfg); // println!("{:#?}", c); let max_bitset = (2_i32.pow((c.size()) as u32) - 1) as u64; @@ -106,7 +103,7 @@ fn generate_provisioners(n: usize) -> Provisioners { .map(|data| SecretKey::from_slice(&data[..]).expect("valid secret key")) .collect(); - let mut p = Provisioners::new(); + let mut p = Provisioners::default(); for (i, sk) in sks.iter().enumerate().skip(1) { let stake_value = 1000 * (i) as u64 * DUSK; let pubkey_bls = PublicKey::new(BlsPublicKey::from(sk)); diff --git a/node/benches/accept.rs b/node/benches/accept.rs index 31c7df8881..f245bf59b6 100644 --- a/node/benches/accept.rs +++ b/node/benches/accept.rs @@ -37,17 +37,14 @@ fn create_step_votes( block_hash: [u8; 32], step: u8, iteration: u8, - provisioners: Provisioners, + provisioners: &Provisioners, keys: &[(PublicKey, BlsSecretKey)], ) -> StepVotes { let sortition_config = SortitionConfig::new(seed, round, iteration * 3 + step, 64); - let committee = Committee::new( - PublicKey::default(), - &mut provisioners.clone(), - sortition_config, - ); + let committee = + Committee::new(PublicKey::default(), &provisioners, &sortition_config); let hdr = message::Header { round, @@ -87,7 +84,7 @@ pub fn verify_block_cert(c: &mut Criterion) { for input in INPUTS { group.measurement_time(Duration::from_secs(input.measurement_time)); let mut keys = vec![]; - let mut provisioners = Provisioners::new(); + let mut provisioners = Provisioners::default(); let rng = &mut StdRng::seed_from_u64(0xbeef); for _ in 0..input.provisioners { let sk = BlsSecretKey::random(rng); @@ -99,7 +96,6 @@ pub fn verify_block_cert(c: &mut Criterion) { let height = 1; let seed = Signature([5; 48]); let block_hash = [1; 32]; - let curr_public_key = keys.first().unwrap().0.clone(); let iteration = 0; let mut cert = Certificate::default(); @@ -109,7 +105,7 @@ pub fn verify_block_cert(c: &mut Criterion) { block_hash, 1, iteration, - provisioners.clone(), + &provisioners, &keys[..], ); cert.second_reduction = create_step_votes( @@ -118,7 +114,7 @@ pub fn verify_block_cert(c: &mut Criterion) { block_hash, 2, iteration, - provisioners.clone(), + &provisioners, &keys[..], ); group.bench_function( @@ -131,7 +127,6 @@ pub fn verify_block_cert(c: &mut Criterion) { chain::verify_block_cert( seed, &provisioners, - &curr_public_key, block_hash, height, &cert, diff --git a/node/src/chain/acceptor.rs b/node/src/chain/acceptor.rs index 52950d35ce..a5b24dcce2 100644 --- a/node/src/chain/acceptor.rs +++ b/node/src/chain/acceptor.rs @@ -98,7 +98,7 @@ impl Acceptor { acc.task.write().await.spawn( mrb, - &provisioners_list.clone(), + provisioners_list, &db, &vm, &network, @@ -189,12 +189,11 @@ impl Acceptor { if iteration == 0 { return; } - let mut prov = provisioners_list.clone(); for iter in 0..iteration { let committee_keys = Committee::new( node_data::bls::PublicKey::default(), - &mut prov, - sortition::Config { + provisioners_list, + &sortition::Config { committee_size: SELECTION_COMMITTEE_SIZE, round, seed, @@ -229,7 +228,6 @@ impl Acceptor { enable_consensus: bool, ) -> anyhow::Result<()> { let mut task = self.task.write().await; - let (_, public_key) = task.keys.clone(); let mut mrb = self.mrb.write().await; let mut provisioners_list = self.provisioners_list.write().await; @@ -239,8 +237,7 @@ impl Acceptor { verify_block_header( self.db.clone(), &mrb.header().clone(), - provisioners_list.clone(), - &public_key, + &provisioners_list, blk.header(), ) .await?; @@ -281,14 +278,10 @@ impl Acceptor { blk.header().height, ); - // Update provisioners list - let updated_provisioners = { - Self::needs_update(blk, &txs).then(|| vm.get_provisioners()) - }; - - if let Some(updated_prov) = updated_provisioners { - *provisioners_list = updated_prov?; - }; + if Self::needs_update(blk, &txs) { + // Update provisioners list + *provisioners_list = vm.get_provisioners()?; + } // Update most_recent_block *mrb = blk.clone(); @@ -455,8 +448,7 @@ impl Acceptor { pub(crate) async fn verify_block_header( db: Arc>, mrb: &ledger::Header, - mrb_eligible_provisioners: Provisioners, - public_key: &node_data::bls::PublicKey, + mrb_eligible_provisioners: &Provisioners, new_blk: &ledger::Header, ) -> anyhow::Result<()> { if new_blk.version > 0 { @@ -510,7 +502,6 @@ pub(crate) async fn verify_block_header( verify_block_cert( prev_block_seed, prev_eligible_provisioners, - public_key, mrb.hash, mrb.height, &new_blk.prev_block_cert, @@ -531,8 +522,7 @@ pub(crate) async fn verify_block_header( verify_block_cert( mrb.seed, - &mrb_eligible_provisioners, - public_key, + mrb_eligible_provisioners, [0u8; 32], new_blk.height, cert, @@ -546,8 +536,7 @@ pub(crate) async fn verify_block_header( // Verify Certificate verify_block_cert( mrb.seed, - &mrb_eligible_provisioners, - public_key, + mrb_eligible_provisioners, new_blk.hash, new_blk.height, &new_blk.cert, @@ -557,11 +546,9 @@ pub(crate) async fn verify_block_header( .await } -#[allow(clippy::too_many_arguments)] pub async fn verify_block_cert( curr_seed: Signature, curr_eligible_provisioners: &Provisioners, - curr_public_key: &node_data::bls::PublicKey, block_hash: [u8; 32], height: u64, cert: &ledger::Certificate, @@ -569,13 +556,13 @@ pub async fn verify_block_cert( enable_quorum_check: bool, ) -> anyhow::Result<()> { let committee = Arc::new(Mutex::new(CommitteeSet::new( - curr_public_key.clone(), + node_data::bls::PublicKey::default(), curr_eligible_provisioners.clone(), ))); let hdr = node_data::message::Header { topic: 0, - pubkey_bls: curr_public_key.clone(), + pubkey_bls: node_data::bls::PublicKey::default(), round: height, step: iteration.step_from_name(StepName::SecondRed), block_hash, diff --git a/node/src/chain/fallback.rs b/node/src/chain/fallback.rs index f3e8c3fc2b..77823edcaa 100644 --- a/node/src/chain/fallback.rs +++ b/node/src/chain/fallback.rs @@ -98,8 +98,7 @@ impl<'a, N: Network, DB: database::DB, VM: vm::VMExecution> acceptor::verify_block_header( self.acc.db.clone(), prev_block.header(), - provisioners_list.clone(), - &PublicKey::default(), + &provisioners_list, blk.header(), ) .await diff --git a/rusk/src/lib/vm.rs b/rusk/src/lib/vm.rs index ed6dbe95a5..517be30ec8 100644 --- a/rusk/src/lib/vm.rs +++ b/rusk/src/lib/vm.rs @@ -6,7 +6,7 @@ use dusk_bytes::DeserializableSlice; use dusk_consensus::contract_state::{CallParams, VerificationOutput}; -use dusk_consensus::user::provisioners::{Member, Provisioners}; +use dusk_consensus::user::provisioners::Provisioners; use dusk_consensus::user::stake::Stake; use node::vm::VMExecution; use node_data::ledger::{Block, SpentTransaction, Transaction}; @@ -139,21 +139,14 @@ impl VMExecution for Rusk { .into_iter() .filter_map(|(key, stake)| { stake.amount.map(|(value, eligibility)| { - let mut m = - Member::new(node_data::bls::PublicKey::new(key)); - m.add_stake(Stake::new(value, stake.reward, eligibility)); - m + let stake = Stake::new(value, stake.reward, eligibility); + let pubkey_bls = node_data::bls::PublicKey::new(key); + (pubkey_bls, stake) }) }); - let mut ret = Provisioners::new(); - for p in provisioners { - let first_stake = p - .first_stake() - .expect("Provisioners must have at least one stake"); - ret.add_member_with_stake( - p.public_key().clone(), - first_stake.clone(), - ); + let mut ret = Provisioners::default(); + for (pubkey_bls, stake) in provisioners { + ret.add_member_with_stake(pubkey_bls, stake); } Ok(ret)