diff --git a/consensus/src/consensus.rs b/consensus/src/consensus.rs index 58158130d9..beb951893c 100644 --- a/consensus/src/consensus.rs +++ b/consensus/src/consensus.rs @@ -6,7 +6,7 @@ use crate::commons::{ConsensusError, Database, QuorumMsgSender, RoundUpdate}; use crate::config::CONSENSUS_MAX_ITER; -use crate::contract_state::Operations; +use crate::operations::Operations; use crate::phase::Phase; use node_data::ledger::Block; diff --git a/consensus/src/execution_ctx.rs b/consensus/src/execution_ctx.rs index c7c7a883fc..9be381692c 100644 --- a/consensus/src/execution_ctx.rs +++ b/consensus/src/execution_ctx.rs @@ -6,10 +6,10 @@ use crate::commons::{ConsensusError, Database, QuorumMsgSender, RoundUpdate}; -use crate::contract_state::Operations; use crate::iteration_ctx::IterationCtx; use crate::msg_handler::HandleMsgOutput::{Pending, Ready}; use crate::msg_handler::MsgHandler; +use crate::operations::Operations; use crate::queue::Queue; use crate::step_votes_reg::SafeCertificateInfoRegistry; use crate::user::committee::Committee; diff --git a/consensus/src/lib.rs b/consensus/src/lib.rs index d864d897d7..a74c0360c4 100644 --- a/consensus/src/lib.rs +++ b/consensus/src/lib.rs @@ -12,9 +12,9 @@ pub mod user; mod aggregator; pub mod config; -pub mod contract_state; mod execution_ctx; mod msg_handler; +pub mod operations; mod phase; mod proposal; mod queue; diff --git a/consensus/src/contract_state.rs b/consensus/src/operations.rs similarity index 87% rename from consensus/src/contract_state.rs rename to consensus/src/operations.rs index df55e4680b..83af1b4bad 100644 --- a/consensus/src/contract_state.rs +++ b/consensus/src/operations.rs @@ -6,7 +6,7 @@ use std::fmt; -use node_data::ledger::{SpentTransaction, Transaction}; +use node_data::ledger::{Header, SpentTransaction, Transaction}; pub type StateRoot = [u8; 32]; pub type EventHash = [u8; 32]; @@ -49,6 +49,12 @@ impl fmt::Display for VerificationOutput { #[async_trait::async_trait] pub trait Operations: Send + Sync { + async fn verify_block_header( + &self, + candidate_header: &Header, + disable_winning_cert_check: bool, + ) -> Result<(), Error>; + async fn verify_state_transition( &self, params: CallParams, diff --git a/consensus/src/phase.rs b/consensus/src/phase.rs index 44c1a34fdb..9603f38923 100644 --- a/consensus/src/phase.rs +++ b/consensus/src/phase.rs @@ -5,8 +5,8 @@ // Copyright (c) DUSK NETWORK. All rights reserved. use crate::commons::{ConsensusError, Database}; -use crate::contract_state::Operations; use crate::execution_ctx::ExecutionCtx; +use crate::operations::Operations; use node_data::message::Message; use node_data::StepName; diff --git a/consensus/src/proposal/block_generator.rs b/consensus/src/proposal/block_generator.rs index 801ae386be..97ca4a9e32 100644 --- a/consensus/src/proposal/block_generator.rs +++ b/consensus/src/proposal/block_generator.rs @@ -5,12 +5,12 @@ // Copyright (c) DUSK NETWORK. All rights reserved. use crate::commons::RoundUpdate; -use crate::contract_state::CallParams; +use crate::operations::CallParams; use node_data::ledger::{to_str, Block, Certificate, IterationsInfo, Seed}; use crate::config; -use crate::contract_state::Operations; use crate::merkle::merkle_root; +use crate::operations::Operations; use dusk_bytes::Serializable; use node_data::ledger; @@ -35,7 +35,7 @@ impl Generator { ru: &RoundUpdate, iteration: u8, failed_iterations: Vec>, - ) -> Result { + ) -> Result { // Sign seed let seed = ru .secret_key @@ -82,7 +82,7 @@ impl Generator { seed: Seed, iteration: u8, failed_iterations: Vec>, - ) -> Result { + ) -> Result { let start_time = Instant::now(); let call_params = CallParams { diff --git a/consensus/src/proposal/step.rs b/consensus/src/proposal/step.rs index 296dd43cff..ce2919150d 100644 --- a/consensus/src/proposal/step.rs +++ b/consensus/src/proposal/step.rs @@ -5,9 +5,9 @@ // Copyright (c) DUSK NETWORK. All rights reserved. use crate::commons::{ConsensusError, Database}; -use crate::contract_state::Operations; use crate::execution_ctx::ExecutionCtx; use crate::msg_handler::{HandleMsgOutput, MsgHandler}; +use crate::operations::Operations; use node_data::message::Message; use std::cmp; use std::sync::Arc; @@ -44,7 +44,7 @@ impl ProposalStep { round: u64, iteration: u8, ) { - debug!(event = "init", name = self.name(), round, iteration,) + debug!(event = "init", name = self.name(), round, iter = iteration,) } pub async fn run( diff --git a/consensus/src/ratification/step.rs b/consensus/src/ratification/step.rs index a088f85303..ad4b8dec00 100644 --- a/consensus/src/ratification/step.rs +++ b/consensus/src/ratification/step.rs @@ -5,8 +5,8 @@ // Copyright (c) DUSK NETWORK. All rights reserved. use crate::commons::{ConsensusError, Database, RoundUpdate}; -use crate::contract_state::Operations; use crate::execution_ctx::ExecutionCtx; +use crate::operations::Operations; use std::marker::PhantomData; use crate::msg_handler::{HandleMsgOutput, MsgHandler}; @@ -18,7 +18,7 @@ use node_data::message::{AsyncQueue, Message, Payload, Topics}; use std::sync::Arc; use tokio::sync::Mutex; -use tracing::{debug, error}; +use tracing::{error, info, Instrument}; pub struct RatificationStep { handler: Arc>, @@ -54,12 +54,8 @@ impl RatificationStep { }, ); - debug!( - event = "voting", - vtype = "ratification", - hash = to_str(&result.hash), - validation_bitset = result.sv.bitset - ); + // Publish ratification vote + info!(event = "send_vote", validation_bitset = result.sv.bitset); // Publish outbound.send(msg.clone()).await.unwrap_or_else(|err| { @@ -106,7 +102,7 @@ impl RatificationStep { event = "init", name = self.name(), round = round, - iteration = iteration, + iter = iteration, hash = to_str(&handler.validation_result().hash), fsv_bitset = handler.validation_result().sv.bitset, quorum_type = format!("{:?}", handler.validation_result().quorum) @@ -123,6 +119,7 @@ impl RatificationStep { if ctx.am_member(committee) { let mut handler = self.handler.lock().await; + let hash = to_str(&handler.validation_result().hash); let vote_msg = Self::try_vote( &ctx.round_update, @@ -130,6 +127,7 @@ impl RatificationStep { handler.validation_result(), ctx.outbound.clone(), ) + .instrument(tracing::info_span!("ratification", hash,)) .await; // Collect my own vote diff --git a/consensus/src/validation/step.rs b/consensus/src/validation/step.rs index 0716033f1d..7012bb3f39 100644 --- a/consensus/src/validation/step.rs +++ b/consensus/src/validation/step.rs @@ -6,8 +6,8 @@ use crate::commons::{ConsensusError, Database, RoundUpdate}; use crate::config; -use crate::contract_state::{CallParams, Operations}; use crate::execution_ctx::ExecutionCtx; +use crate::operations::{CallParams, Operations}; use crate::validation::handler; use anyhow::anyhow; use dusk_bytes::DeserializableSlice; @@ -17,7 +17,7 @@ use node_data::message::{self, AsyncQueue, Message, Payload, Topics}; use std::sync::Arc; use tokio::sync::Mutex; use tokio::task::JoinSet; -use tracing::{debug, error, Instrument}; +use tracing::{debug, error, info, Instrument}; pub struct ValidationStep { handler: Arc>, @@ -42,7 +42,7 @@ impl ValidationStep { ) .await } - .instrument(tracing::info_span!("voting", hash)), + .instrument(tracing::info_span!("validation", hash,)), ); } @@ -54,20 +54,42 @@ impl ValidationStep { inbound: AsyncQueue, executor: Arc>, ) { - // TODO: Verify Block Header - let hash = candidate.header().hash; - - // Call VST for non-empty blocks - if hash != [0u8; 32] { - if let Err(err) = Self::call_vst(candidate, ru, executor).await { - error!( - event = "failed_vst_call", - reason = format!("{:?}", err) - ); - return; - } + let header = candidate.header(); + + // A Validation step with empty/default Block produces a Nil Vote + if header.hash == [0u8; 32] { + Self::cast_vote([0u8; 32], ru, iteration, outbound, inbound).await; + return; } + // Verify candidate header (all fields except the winning certificate) + // NB: Winning certificate is produced only on reaching consensus + if let Err(err) = executor + .lock() + .await + .verify_block_header(header, true) + .await + { + error!(event = "invalid_header", ?err, ?header); + return; + }; + + // Call Verify State Transition to make sure transactions set is valid + if let Err(err) = Self::call_vst(candidate, ru, executor).await { + error!(event = "failed_vst_call", ?err); + return; + } + + Self::cast_vote(header.hash, ru, iteration, outbound, inbound).await; + } + + async fn cast_vote( + hash: [u8; 32], + ru: &RoundUpdate, + iteration: u8, + outbound: AsyncQueue, + inbound: AsyncQueue, + ) { let hdr = message::Header { pubkey_bls: ru.pubkey_bls.clone(), round: ru.round, @@ -85,7 +107,7 @@ impl ValidationStep { ); // Publish validation vote - debug!(event = "voting", vtype = "validation", hash = to_str(&hash)); + info!(event = "send_vote"); // Publish outbound.send(msg.clone()).await.unwrap_or_else(|err| { @@ -183,7 +205,7 @@ impl ValidationStep { event = "init", name = self.name(), round, - iteration, + iter = iteration, hash = to_str(&handler.candidate.header().hash), ) } diff --git a/node/src/chain.rs b/node/src/chain.rs index 61b2709bec..856f253b6d 100644 --- a/node/src/chain.rs +++ b/node/src/chain.rs @@ -10,12 +10,13 @@ mod fallback; mod fsm; mod genesis; +mod header_validation; + use self::acceptor::Acceptor; use self::fsm::SimpleFSM; use crate::database::Ledger; use crate::{database, vm, Network}; use crate::{LongLivedService, Message}; -pub use acceptor::verify_block_cert; use anyhow::Result; use async_trait::async_trait; use dusk_consensus::commons::ConsensusError; diff --git a/node/src/chain/acceptor.rs b/node/src/chain/acceptor.rs index f876c5cd05..9bf8d751e3 100644 --- a/node/src/chain/acceptor.rs +++ b/node/src/chain/acceptor.rs @@ -9,22 +9,19 @@ use crate::{vm, Message, Network}; use anyhow::{anyhow, Result}; use dusk_consensus::commons::ConsensusError; use dusk_consensus::config::CONSENSUS_ROLLING_FINALITY_THRESHOLD; -use dusk_consensus::user::committee::CommitteeSet; use dusk_consensus::user::provisioners::{ContextProvisioners, Provisioners}; use node_data::ledger::{ - self, to_str, Block, BlockWithLabel, Label, Seed, Signature, - SpentTransaction, + self, to_str, Block, BlockWithLabel, Label, Seed, SpentTransaction, }; use node_data::message::AsyncQueue; use node_data::message::Payload; -use node_data::StepName; + use std::sync::Arc; use tokio::sync::RwLock; use tracing::{info, warn}; -use dusk_consensus::quorum::verifiers::{self, QuorumResult}; - use super::consensus::Task; +use crate::chain::header_validation::Validator; #[allow(dead_code)] pub(crate) enum RevertTarget { @@ -132,11 +129,11 @@ impl Acceptor { } async fn spawn_task(&self) { - let provisioners = self.provisioners_list.read().await.to_current(); + let provisioners_list = self.provisioners_list.read().await.clone(); self.task.write().await.spawn( self.mrb.read().await.inner(), - Arc::new(provisioners), + provisioners_list, &self.db, &self.vm, &self.network, @@ -374,7 +371,7 @@ impl Acceptor { if enable_consensus { task.spawn( mrb.inner(), - Arc::new(provisioners_list.to_current()), + provisioners_list.clone(), &self.db, &self.vm, &self.network, @@ -473,8 +470,7 @@ impl Acceptor { pub(crate) async fn restart_consensus(&mut self) { let mut task = self.task.write().await; let mrb = self.mrb.read().await; - let provisioners_list = - self.provisioners_list.read().await.to_current(); + let provisioners_list = self.provisioners_list.read().await.clone(); task.abort_with_wait().await; info!( @@ -486,7 +482,7 @@ impl Acceptor { task.spawn( mrb.inner(), - Arc::new(provisioners_list), + provisioners_list, &self.db, &self.vm, &self.network, @@ -552,178 +548,9 @@ pub(crate) async fn verify_block_header( db: Arc>, mrb: &ledger::Header, provisioners: &ContextProvisioners, - new_blk: &ledger::Header, + candidate_header: &ledger::Header, ) -> anyhow::Result { - let mrb_eligible_provisioners = provisioners.current(); - let prev_eligible_provisioners = provisioners.prev(); - if new_blk.version > 0 { - return Err(anyhow!("unsupported block version")); - } - - if new_blk.hash == [0u8; 32] { - return Err(anyhow!("empty block hash")); - } - - if new_blk.height != mrb.height + 1 { - return Err(anyhow!( - "invalid block height block_height: {:?}, curr_height: {:?}", - new_blk.height, - mrb.height, - )); - } - - if new_blk.prev_block_hash != mrb.hash { - return Err(anyhow!("invalid previous block hash")); - } - - // Ensure block is not already in the ledger - db.read().await.view(|v| { - if Ledger::get_block_exists(&v, &new_blk.hash)? { - return Err(anyhow!("block already exists")); - } - - Ok(()) - })?; - - // Verify prev_block_cert field - if mrb.height >= 1 { - let prev_block_seed = db.read().await.view(|v| { - let prev_block = Ledger::fetch_block_by_height(&v, mrb.height - 1)? - .ok_or_else(|| anyhow::anyhow!("could not fetch block"))?; - - Ok::<_, anyhow::Error>(prev_block.header().seed) - })?; - - // Terms in use - // genesis_blk -> ... -> prev_block -> most_recent_block(mrb) -> new_blk - // (pending to be accepted) - verify_block_cert( - prev_block_seed, - prev_eligible_provisioners, - mrb.hash, - mrb.height, - &new_blk.prev_block_cert, - mrb.iteration, - true, - ) - .await?; - } - - // Verify Failed iterations - let mut attested = true; - - for (iter, cert) in new_blk.failed_iterations.cert_list.iter().enumerate() { - if let Some(cert) = cert { - info!(event = "verify_cert", cert_type = "failed_cert", iter); - - let quorums = verify_block_cert( - mrb.seed, - mrb_eligible_provisioners, - [0u8; 32], - new_blk.height, - cert, - iter as u8, - false, - ) - .await?; - - attested = attested - && quorums.0.quorum_reached() - && quorums.1.quorum_reached(); - } else { - attested = false; - } - } - - // Verify Certificate - verify_block_cert( - mrb.seed, - mrb_eligible_provisioners, - new_blk.hash, - new_blk.height, - &new_blk.cert, - new_blk.iteration, - true, - ) - .await?; - - Ok(attested) -} - -pub async fn verify_block_cert( - curr_seed: Signature, - curr_eligible_provisioners: &Provisioners, - block_hash: [u8; 32], - height: u64, - cert: &ledger::Certificate, - iteration: u8, - enable_quorum_check: bool, -) -> anyhow::Result<(QuorumResult, QuorumResult)> { - let committee = RwLock::new(CommitteeSet::new(curr_eligible_provisioners)); - - let hdr = node_data::message::Header { - topic: node_data::message::Topics::Unknown, - pubkey_bls: node_data::bls::PublicKey::default(), - round: height, - iteration, - block_hash, - }; - - let mut result = (QuorumResult::default(), QuorumResult::default()); - - // Verify validation - match verifiers::verify_step_votes( - &cert.validation, - &committee, - curr_seed, - &hdr, - StepName::Validation, - enable_quorum_check, - ) - .await - { - Ok(validation_quorum_result) => { - result.0 = validation_quorum_result; - } - Err(e) => { - return Err(anyhow!( - "invalid validation, hash = {}, round = {}, iter = {}, seed = {}, sv = {:?}, err = {}", - to_str(&hdr.block_hash), - hdr.round, - iteration, - to_str(&curr_seed.inner()), - cert.validation, - e - )); - } - }; - - // Verify ratification - match verifiers::verify_step_votes( - &cert.ratification, - &committee, - curr_seed, - &hdr, - StepName::Ratification, - enable_quorum_check, - ) - .await - { - Ok(ratification_quorum_result) => { - result.1 = ratification_quorum_result; - } - Err(e) => { - return Err(anyhow!( - "invalid ratification, hash = {}, round = {}, iter = {}, seed = {}, sv = {:?}, err = {}", - to_str(&hdr.block_hash), - hdr.round, - iteration, - to_str(&curr_seed.inner()), - cert.ratification, - e, - )); - } - } + let validator = Validator::new(db, mrb, provisioners); - Ok(result) + validator.execute_checks(candidate_header, false).await } diff --git a/node/src/chain/consensus.rs b/node/src/chain/consensus.rs index 369eb3cba1..02856d9f8a 100644 --- a/node/src/chain/consensus.rs +++ b/node/src/chain/consensus.rs @@ -9,11 +9,11 @@ use crate::{vm, Message, Network}; use async_trait::async_trait; use dusk_consensus::commons::{ConsensusError, RoundUpdate}; use dusk_consensus::consensus::Consensus; -use dusk_consensus::contract_state::{ +use dusk_consensus::operations::{ CallParams, Error, Operations, Output, VerificationOutput, }; -use dusk_consensus::user::provisioners::Provisioners; -use node_data::ledger::{Block, Hash, Transaction}; +use dusk_consensus::user::provisioners::ContextProvisioners; +use node_data::ledger::{Block, Hash, Header, Transaction}; use node_data::message::payload::GetCandidate; use node_data::message::AsyncQueue; use node_data::message::{Payload, Topics}; @@ -21,6 +21,8 @@ use tokio::sync::{oneshot, Mutex, RwLock}; use tokio::task::JoinHandle; use tracing::{error, info, trace, warn}; +use crate::chain::header_validation::Validator; +use node_data::ledger; use std::sync::Arc; use std::time::Duration; @@ -73,17 +75,23 @@ impl Task { pub(crate) fn spawn( &mut self, most_recent_block: &node_data::ledger::Block, - provisioners: Arc, + provisioners_list: ContextProvisioners, db: &Arc>, vm: &Arc>, network: &Arc>, ) { + let current = provisioners_list.to_current(); let c = Consensus::new( self.main_inbound.clone(), self.outbound.clone(), self.quorum_inbound.clone(), self.outbound.clone(), - Arc::new(Mutex::new(Executor::new(db, vm))), + Arc::new(Mutex::new(Executor::new( + db, + vm, + most_recent_block.header().clone(), + provisioners_list, // TODO: Avoid cloning + ))), Arc::new(Mutex::new(CandidateDB::new(db.clone(), network.clone()))), ); @@ -98,8 +106,7 @@ impl Task { self.task_id += 1; - let (all_num, eligible_num) = - provisioners.get_provisioners_info(ru.round); + let (all_num, eligible_num) = current.get_provisioners_info(ru.round); info!( event = "spawn consensus", @@ -115,7 +122,7 @@ impl Task { self.running_task = Some(( tokio::spawn(async move { - let cons_result = c.spin(ru, provisioners, cancel_rx).await; + let cons_result = c.spin(ru, current.into(), cancel_rx).await; if let Err(e) = result_queue.send(cons_result).await { error!("Unable to send consensus result to queue {e}") } @@ -262,24 +269,55 @@ impl dusk_consensus::commons::Database pub struct Executor { db: Arc>, vm: Arc>, + mrb_header: ledger::Header, + provisioners: ContextProvisioners, } impl Executor { - fn new(db: &Arc>, vm: &Arc>) -> Self { + fn new( + db: &Arc>, + vm: &Arc>, + mrb_header: ledger::Header, + provisioners: ContextProvisioners, + ) -> Self { Executor { db: db.clone(), vm: vm.clone(), + mrb_header, + provisioners, } } } #[async_trait::async_trait] impl Operations for Executor { + async fn verify_block_header( + &self, + candidate_header: &Header, + disable_winning_cert_check: bool, + ) -> Result<(), Error> { + let validator = Validator::new( + self.db.clone(), + &self.mrb_header, + &self.provisioners, + ); + + validator + .execute_checks(candidate_header, disable_winning_cert_check) + .await + .map_err(|err| { + error!("failed to verify header {}", err); + Error::Failed + })?; + + Ok(()) + } + async fn verify_state_transition( &self, params: CallParams, txs: Vec, - ) -> Result { + ) -> Result { info!("verifying state"); let vm = self.vm.read().await; diff --git a/node/src/chain/header_validation.rs b/node/src/chain/header_validation.rs new file mode 100644 index 0000000000..7cce97835b --- /dev/null +++ b/node/src/chain/header_validation.rs @@ -0,0 +1,261 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) DUSK NETWORK. All rights reserved. + +use crate::database; +use crate::database::Ledger; +use anyhow::anyhow; +use dusk_consensus::quorum::verifiers; +use dusk_consensus::quorum::verifiers::QuorumResult; +use dusk_consensus::user::committee::CommitteeSet; +use dusk_consensus::user::provisioners::{ContextProvisioners, Provisioners}; +use node_data::ledger::to_str; +use node_data::ledger::Signature; +use node_data::{ledger, StepName}; +use std::sync::Arc; +use tokio::sync::RwLock; +use tracing::info; + +/// An implementation of the all validation checks of a candidate block header +/// according to current context +pub(crate) struct Validator<'a, DB: database::DB> { + pub(crate) db: Arc>, + block: &'a ledger::Header, + provisioners: &'a ContextProvisioners, +} + +impl<'a, DB: database::DB> Validator<'a, DB> { + pub fn new( + db: Arc>, + block: &'a ledger::Header, + provisioners: &'a ContextProvisioners, + ) -> Self { + Self { + db, + block, + provisioners, + } + } + + /// Executes check points to make sure a candidate header is fully valid + /// + /// * `disable_winner_cert_check` - disables the check of the winning + /// certificate + pub async fn execute_checks( + &self, + candidate_block: &'a ledger::Header, + disable_winner_cert_check: bool, + ) -> anyhow::Result { + self.verify_basic_fields(candidate_block).await?; + self.verify_prev_block_cert(candidate_block).await?; + + if !disable_winner_cert_check { + self.verify_winning_cert(candidate_block).await?; + } + + self.verify_failed_iterations(candidate_block).await + } + + /// Verifies any non-certificate field + pub async fn verify_basic_fields( + &self, + candidate_block: &'a ledger::Header, + ) -> anyhow::Result<()> { + if candidate_block.version > 0 { + return Err(anyhow!("unsupported block version")); + } + + if candidate_block.hash == [0u8; 32] { + return Err(anyhow!("empty block hash")); + } + + if candidate_block.height != self.block.height + 1 { + return Err(anyhow!( + "invalid block height block_height: {:?}, curr_height: {:?}", + candidate_block.height, + self.block.height, + )); + } + + if candidate_block.prev_block_hash != self.block.hash { + return Err(anyhow!("invalid previous block hash")); + } + + // Ensure block is not already in the ledger + self.db.read().await.view(|v| { + if Ledger::get_block_exists(&v, &candidate_block.hash)? { + return Err(anyhow!("block already exists")); + } + + Ok(()) + })?; + + Ok(()) + } + + pub async fn verify_prev_block_cert( + &self, + candidate_block: &'a ledger::Header, + ) -> anyhow::Result<()> { + if self.block.height == 0 { + return Ok(()); + } + + let prev_block_seed = self.db.read().await.view(|v| { + let prev_block = + Ledger::fetch_block_by_height(&v, self.block.height - 1)? + .ok_or_else(|| anyhow::anyhow!("could not fetch block"))?; + + Ok::<_, anyhow::Error>(prev_block.header().seed) + })?; + + verify_block_cert( + prev_block_seed, + self.provisioners.prev(), + self.block.hash, + self.block.height, + &candidate_block.prev_block_cert, + self.block.iteration, + true, + ) + .await?; + + Ok(()) + } + + pub async fn verify_failed_iterations( + &self, + candidate_block: &'a ledger::Header, + ) -> anyhow::Result { + // Verify Failed iterations + let mut attested = true; + + for (iter, cert) in candidate_block + .failed_iterations + .cert_list + .iter() + .enumerate() + { + if let Some(cert) = cert { + info!(event = "verify_cert", cert_type = "failed_cert", iter); + + let quorums = verify_block_cert( + self.block.seed, + self.provisioners.current(), + [0u8; 32], + candidate_block.height, + cert, + iter as u8, + false, + ) + .await?; + + attested = attested + && quorums.0.quorum_reached() + && quorums.1.quorum_reached(); + } else { + attested = false; + } + } + + Ok(attested) + } + + pub async fn verify_winning_cert( + &self, + candidate_block: &'a ledger::Header, + ) -> anyhow::Result<()> { + verify_block_cert( + self.block.seed, + self.provisioners.current(), + candidate_block.hash, + candidate_block.height, + &candidate_block.cert, + candidate_block.iteration, + true, + ) + .await?; + + Ok(()) + } +} + +pub async fn verify_block_cert( + curr_seed: Signature, + curr_eligible_provisioners: &Provisioners, + block_hash: [u8; 32], + height: u64, + cert: &ledger::Certificate, + iteration: u8, + enable_quorum_check: bool, +) -> anyhow::Result<(QuorumResult, QuorumResult)> { + let committee = RwLock::new(CommitteeSet::new(curr_eligible_provisioners)); + + let hdr = node_data::message::Header { + topic: node_data::message::Topics::Unknown, + pubkey_bls: node_data::bls::PublicKey::default(), + round: height, + iteration, + block_hash, + }; + + let mut result = (QuorumResult::default(), QuorumResult::default()); + + // Verify validation + match verifiers::verify_step_votes( + &cert.validation, + &committee, + curr_seed, + &hdr, + StepName::Validation, + enable_quorum_check, + ) + .await + { + Ok(validation_quorum_result) => { + result.0 = validation_quorum_result; + } + Err(e) => { + return Err(anyhow!( + "invalid validation, hash = {}, round = {}, iter = {}, seed = {}, sv = {:?}, err = {}", + to_str(&hdr.block_hash), + hdr.round, + iteration, + to_str(&curr_seed.inner()), + cert.validation, + e + )); + } + }; + + // Verify ratification + match verifiers::verify_step_votes( + &cert.ratification, + &committee, + curr_seed, + &hdr, + StepName::Ratification, + enable_quorum_check, + ) + .await + { + Ok(ratification_quorum_result) => { + result.1 = ratification_quorum_result; + } + Err(e) => { + return Err(anyhow!( + "invalid ratification, hash = {}, round = {}, iter = {}, seed = {}, sv = {:?}, err = {}", + to_str(&hdr.block_hash), + hdr.round, + iteration, + to_str(&curr_seed.inner()), + cert.ratification, + e, + )); + } + } + + Ok(result) +} diff --git a/node/src/vm.rs b/node/src/vm.rs index c7d1998cd6..e77845c87a 100644 --- a/node/src/vm.rs +++ b/node/src/vm.rs @@ -5,7 +5,7 @@ // Copyright (c) DUSK NETWORK. All rights reserved. use dusk_consensus::{ - contract_state::CallParams, contract_state::VerificationOutput, + operations::CallParams, operations::VerificationOutput, user::provisioners::Provisioners, }; use node_data::ledger::{Block, SpentTransaction, Transaction}; diff --git a/node/testbed.sh b/node/testbed.sh index 8dc5da859b..c7199c705b 100755 --- a/node/testbed.sh +++ b/node/testbed.sh @@ -51,7 +51,7 @@ RUSK_STATE_PATH=${RUSK_STATE_PATH} cargo r --release -p rusk -- recovery-state --init $GENESIS_PATH echo "starting node $ID ..." echo "${KEYS_PATH}/node_$ID.keys" - RUSK_STATE_PATH=${RUSK_STATE_PATH} ./target/release/rusk --kadcast-bootstrap "$BOOTSTRAP_ADDR" --kadcast-public-address "$PUBLIC_ADDR" --log-level="$LOG_LEVEL" --consensus-keys-path="${KEYS_PATH}/node_$ID.keys" --db-path="$NODE_FOLDER" --http-listen-addr "$WS_LISTEN_ADDR" --delay-on-resp-msg=10 > "${TEMPD}/node_${ID}.log" & + RUSK_STATE_PATH=${RUSK_STATE_PATH} ./target/release/rusk --kadcast-bootstrap "$BOOTSTRAP_ADDR" --kadcast-public-address "$PUBLIC_ADDR" --log-level="$LOG_LEVEL" --log-filter="dusk_consensus=debug" --consensus-keys-path="${KEYS_PATH}/node_$ID.keys" --db-path="$NODE_FOLDER" --http-listen-addr "$WS_LISTEN_ADDR" --delay-on-resp-msg=10 > "${TEMPD}/node_${ID}.log" & } ## Use ~/.cargo/bin/tokio-console --retain-for 0s http://127.0.0.1:10000 to connect console to first node @@ -69,7 +69,7 @@ RUST_LOG="info" TOKIO_CONSOLE_BIND="127.0.0.1:$T_BIND_PORT" \ cargo --config 'build.rustflags = ["--cfg", "tokio_unstable"]' run --features with_telemetry --bin rusk-node --\ - --kadcast_bootstrap "$BOOTSTRAP_ADDR" --kadcast_public_address "$PUBLIC_ADDR" --log-level="$LOG_LEVEL" \ + --kadcast_bootstrap "$BOOTSTRAP_ADDR" --kadcast_public_address "$PUBLIC_ADDR" --log-level="$LOG_LEVEL" --log-filter="dusk_consensus=debug" \ --consensus-keys-path="${KEYS_PATH}/node_$ID.keys" --db-path="${TEMPD}/db/${ID}" --config="default.config.toml" > "${TEMPD}/node_${ID}.log" & } diff --git a/rusk/src/lib/error.rs b/rusk/src/lib/error.rs index 40761b9366..d5986e2e75 100644 --- a/rusk/src/lib/error.rs +++ b/rusk/src/lib/error.rs @@ -7,7 +7,7 @@ use std::{fmt, io}; use dusk_bls12_381::BlsScalar; -use dusk_consensus::contract_state::VerificationOutput; +use dusk_consensus::operations::VerificationOutput; use rusk_abi::dusk::Dusk; #[derive(Debug)] diff --git a/rusk/src/lib/lib.rs b/rusk/src/lib/lib.rs index fdf0e74c92..3e0405d4c0 100644 --- a/rusk/src/lib/lib.rs +++ b/rusk/src/lib/lib.rs @@ -28,7 +28,7 @@ use tracing::{error, info}; use bytecheck::CheckBytes; use dusk_bls12_381::BlsScalar; use dusk_bls12_381_sign::PublicKey as BlsPublicKey; -use dusk_consensus::contract_state::VerificationOutput; +use dusk_consensus::operations::VerificationOutput; use dusk_pki::{PublicKey, ViewKey}; use node_data::ledger::{SpentTransaction, Transaction}; use parking_lot::{Mutex, MutexGuard}; diff --git a/rusk/src/lib/vm.rs b/rusk/src/lib/vm.rs index 36f2b2c966..8694d0c408 100644 --- a/rusk/src/lib/vm.rs +++ b/rusk/src/lib/vm.rs @@ -5,7 +5,7 @@ // Copyright (c) DUSK NETWORK. All rights reserved. use dusk_bytes::DeserializableSlice; -use dusk_consensus::contract_state::{CallParams, VerificationOutput}; +use dusk_consensus::operations::{CallParams, VerificationOutput}; use dusk_consensus::user::provisioners::Provisioners; use dusk_consensus::user::stake::Stake; use node::vm::VMExecution; diff --git a/rusk/tests/common/state.rs b/rusk/tests/common/state.rs index 0cc3fb05fb..b202ad1142 100644 --- a/rusk/tests/common/state.rs +++ b/rusk/tests/common/state.rs @@ -11,7 +11,7 @@ use rusk::{Result, Rusk}; use rusk_recovery_tools::state::{self, Snapshot}; use dusk_bls12_381_sign::PublicKey; -use dusk_consensus::contract_state::CallParams; +use dusk_consensus::operations::CallParams; use dusk_wallet_core::Transaction as PhoenixTransaction; use node_data::ledger::{Block, Header, SpentTransaction}; use tracing::info;