diff --git a/consensus/Cargo.toml b/consensus/Cargo.toml index b03d11f09a..dbeb83a953 100644 --- a/consensus/Cargo.toml +++ b/consensus/Cargo.toml @@ -43,3 +43,8 @@ serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } base64 = "0.13" node-data = { version = "0.1", path = "../node-data", features = ["faker"]} +criterion = "0.5" + +[[bench]] +name = "merkle" +harness = false \ No newline at end of file diff --git a/consensus/benches/merkle.rs b/consensus/benches/merkle.rs new file mode 100644 index 0000000000..35463e3c43 --- /dev/null +++ b/consensus/benches/merkle.rs @@ -0,0 +1,34 @@ +// 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 criterion::{criterion_group, criterion_main, Criterion}; +use dusk_consensus::config::MAX_NUMBER_OF_TRANSACTIONS; +use dusk_consensus::merkle::merkle_root; +use rand::rngs::StdRng; +use rand::RngCore; +use rand::SeedableRng; + +fn merkle(c: &mut Criterion) { + let tx_hashes: Vec<_> = (0..MAX_NUMBER_OF_TRANSACTIONS) + .map(|seed| { + let rng = &mut StdRng::seed_from_u64(seed as u64); + let mut buf = [0u8; 32]; + rng.fill_bytes(&mut buf); + buf + }) + .collect(); + + let label: String = format!("merkle_{}", MAX_NUMBER_OF_TRANSACTIONS); + + c.bench_function(&label, |b| { + b.iter(|| { + let _ = merkle_root(&tx_hashes[..]); + }) + }); +} + +criterion_group!(benches, merkle); +criterion_main!(benches); diff --git a/consensus/src/commons.rs b/consensus/src/commons.rs index 558d5aa63e..c73bd0dbea 100644 --- a/consensus/src/commons.rs +++ b/consensus/src/commons.rs @@ -123,6 +123,8 @@ pub enum ConsensusError { ChildTaskTerminated, Canceled(u64), VoteAlreadyCollected, + TooManyTransactions(usize), + TooManyFaults(usize), } impl From for ConsensusError { diff --git a/consensus/src/config.rs b/consensus/src/config.rs index f93322aedf..763fbd0890 100644 --- a/consensus/src/config.rs +++ b/consensus/src/config.rs @@ -15,6 +15,8 @@ pub const VALIDATION_COMMITTEE_CREDITS: usize = 64; pub const RATIFICATION_COMMITTEE_CREDITS: usize = 64; pub const RELAX_ITERATION_THRESHOLD: u8 = 8; +pub const MAX_NUMBER_OF_TRANSACTIONS: usize = 1_000; +pub const MAX_NUMBER_OF_FAULTS: usize = 100; /// Emergency mode is enabled after 16 iterations pub const EMERGENCY_MODE_ITERATION_THRESHOLD: u8 = 16; diff --git a/consensus/src/proposal/block_generator.rs b/consensus/src/proposal/block_generator.rs index e53a0585e4..d67348d6f9 100644 --- a/consensus/src/proposal/block_generator.rs +++ b/consensus/src/proposal/block_generator.rs @@ -13,7 +13,7 @@ use std::cmp::max; use crate::merkle::merkle_root; -use crate::config::MINIMUM_BLOCK_TIME; +use crate::config::{MAX_NUMBER_OF_FAULTS, MINIMUM_BLOCK_TIME}; use dusk_bytes::Serializable; use node_data::message::payload::Candidate; use node_data::message::{ConsensusHeader, Message, SignInfo, StepMessage}; @@ -92,6 +92,13 @@ impl Generator { faults: &[Fault], voters: &[Voter], ) -> Result { + // Limit number of faults in the block + let faults = if faults.len() > MAX_NUMBER_OF_FAULTS { + &faults[..MAX_NUMBER_OF_FAULTS] + } else { + faults + }; + let to_slash = Slash::from_iterations_and_faults(&failed_iterations, faults)?; diff --git a/consensus/src/proposal/handler.rs b/consensus/src/proposal/handler.rs index 7d2500a3cc..96686b69fe 100644 --- a/consensus/src/proposal/handler.rs +++ b/consensus/src/proposal/handler.rs @@ -5,6 +5,7 @@ // Copyright (c) DUSK NETWORK. All rights reserved. use crate::commons::{ConsensusError, Database, RoundUpdate}; +use crate::config::{MAX_NUMBER_OF_FAULTS, MAX_NUMBER_OF_TRANSACTIONS}; use crate::merkle::merkle_root; use crate::msg_handler::{HandleMsgOutput, MsgHandler}; use crate::user::committee::Committee; @@ -95,6 +96,12 @@ impl ProposalHandler { return Err(ConsensusError::InvalidBlockHash); } + if p.candidate.txs().len() > MAX_NUMBER_OF_TRANSACTIONS { + return Err(ConsensusError::TooManyTransactions( + p.candidate.txs().len(), + )); + } + let tx_hashes: Vec<_> = p.candidate.txs().iter().map(|t| t.hash()).collect(); let tx_root = merkle_root(&tx_hashes[..]); @@ -102,6 +109,12 @@ impl ProposalHandler { return Err(ConsensusError::InvalidBlock); } + if p.candidate.faults().len() > MAX_NUMBER_OF_FAULTS { + return Err(ConsensusError::TooManyFaults( + p.candidate.faults().len(), + )); + } + let fault_hashes: Vec<_> = p.candidate.faults().iter().map(|t| t.hash()).collect(); let fault_root = merkle_root(&fault_hashes[..]); diff --git a/rusk/src/lib/node/rusk.rs b/rusk/src/lib/node/rusk.rs index 39e3095c1b..f53c7657ce 100644 --- a/rusk/src/lib/node/rusk.rs +++ b/rusk/src/lib/node/rusk.rs @@ -18,8 +18,8 @@ use tracing::{debug, info, warn}; use dusk_bytes::{DeserializableSlice, Serializable}; use dusk_consensus::config::{ ratification_extra, ratification_quorum, validation_extra, - validation_quorum, RATIFICATION_COMMITTEE_CREDITS, - VALIDATION_COMMITTEE_CREDITS, + validation_quorum, MAX_NUMBER_OF_TRANSACTIONS, + RATIFICATION_COMMITTEE_CREDITS, VALIDATION_COMMITTEE_CREDITS, }; use dusk_consensus::operations::{CallParams, VerificationOutput, Voter}; use execution_core::{ @@ -131,6 +131,13 @@ impl Rusk { break; } } + + // Limit execution to the block transactions limit + if spent_txs.len() >= MAX_NUMBER_OF_TRANSACTIONS { + info!("Maximum number of transactions reached"); + break; + } + let tx_id = hex::encode(unspent_tx.id()); if unspent_tx.inner.gas_limit() > block_gas_left { info!("Skipping {tx_id} due gas_limit greater than left: {block_gas_left}");