Skip to content

Commit

Permalink
Merge pull request #2302 from dusk-network/block_size
Browse files Browse the repository at this point in the history
consensus: check block size
  • Loading branch information
herr-seppia authored Sep 9, 2024
2 parents 67a6a08 + 7e61d0d commit 1eb38b7
Show file tree
Hide file tree
Showing 16 changed files with 167 additions and 53 deletions.
2 changes: 2 additions & 0 deletions consensus/src/commons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ impl From<BlsSigError> for StepSigError {
pub enum ConsensusError {
InvalidBlock,
InvalidBlockHash,
InvalidBlockSize(usize),
InvalidSignature(BlsSigError),
InvalidMsgType,
InvalidValidationStepVotes(StepSigError),
Expand All @@ -125,6 +126,7 @@ pub enum ConsensusError {
VoteAlreadyCollected,
TooManyTransactions(usize),
TooManyFaults(usize),
UnknownBlockSize,
}

impl From<StepSigError> for ConsensusError {
Expand Down
2 changes: 2 additions & 0 deletions consensus/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub const RELAX_ITERATION_THRESHOLD: u8 = 8;
pub const MAX_NUMBER_OF_TRANSACTIONS: usize = 1_000;
pub const MAX_NUMBER_OF_FAULTS: usize = 100;

pub const MAX_BLOCK_SIZE: usize = 1_024 * 1_024;

/// Emergency mode is enabled after 16 iterations
pub const EMERGENCY_MODE_ITERATION_THRESHOLD: u8 = 16;

Expand Down
1 change: 1 addition & 0 deletions consensus/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ pub struct CallParams {
pub generator_pubkey: node_data::bls::PublicKey,
pub to_slash: Vec<Slash>,
pub voters_pubkey: Option<Vec<Voter>>,
pub max_txs_bytes: usize,
}

#[derive(Default)]
Expand Down
79 changes: 48 additions & 31 deletions consensus/src/proposal/block_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@

use crate::commons::RoundUpdate;
use crate::operations::{CallParams, Operations, Voter};
use node_data::ledger::{
to_str, Attestation, Block, Fault, IterationsInfo, Seed, Slash,
};
use node_data::ledger::{to_str, Block, Fault, IterationsInfo, Seed, Slash};
use std::cmp::max;

use crate::merkle::merkle_root;

use crate::config::{MAX_NUMBER_OF_FAULTS, MINIMUM_BLOCK_TIME};
use crate::config::{MAX_BLOCK_SIZE, 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};
Expand Down Expand Up @@ -99,52 +97,71 @@ impl<T: Operations> Generator<T> {
faults
};

let block_gas_limit = self.executor.get_block_gas_limit().await;
let to_slash =
Slash::from_iterations_and_faults(&failed_iterations, faults)?;

let prev_block_hash = ru.hash();
let mut blk_header = ledger::Header {
version: 0,
height: ru.round,
gas_limit: block_gas_limit,
prev_block_hash,
seed,
generator_bls_pubkey: *ru.pubkey_bls.bytes(),
prev_block_cert: *ru.att(),
iteration,
failed_iterations,
..Default::default()
};

let header_size = blk_header.size().map_err(|e| {
crate::operations::Error::InvalidEST(anyhow::anyhow!(
"Cannot get header size {e}. This should be a bug"
))
})?;

// We always write the faults len in a u32
let mut faults_size = u32::SIZE;
let faults_hashes: Vec<_> = faults
.iter()
.map(|f| {
faults_size += f.size();
f.hash()
})
.collect();

blk_header.faultroot = merkle_root(&faults_hashes);

// We know for sure that this operation cannot underflow
let max_txs_bytes = MAX_BLOCK_SIZE - header_size - faults_size;

let call_params = CallParams {
round: ru.round,
generator_pubkey: ru.pubkey_bls.clone(),
to_slash,
voters_pubkey: Some(voters.to_owned()),
max_txs_bytes,
};

let result =
self.executor.execute_state_transition(call_params).await?;

let block_gas_limit = self.executor.get_block_gas_limit().await;
blk_header.state_hash = result.verification_output.state_root;
blk_header.event_hash = result.verification_output.event_hash;

let tx_hashes: Vec<_> =
result.txs.iter().map(|t| t.inner.hash()).collect();
let txs: Vec<_> = result.txs.into_iter().map(|t| t.inner).collect();
let txroot = merkle_root(&tx_hashes[..]);
blk_header.txroot = merkle_root(&tx_hashes[..]);

let faults = Vec::<Fault>::new();
let faults_hashes: Vec<_> = faults.iter().map(|f| f.hash()).collect();
let faultroot = merkle_root(&faults_hashes);
let timestamp =
blk_header.timestamp =
max(ru.timestamp() + MINIMUM_BLOCK_TIME, get_current_timestamp());

let prev_block_hash = ru.hash();
let blk_header = ledger::Header {
version: 0,
height: ru.round,
timestamp,
gas_limit: block_gas_limit,
prev_block_hash,
seed,
generator_bls_pubkey: *ru.pubkey_bls.bytes(),
state_hash: result.verification_output.state_root,
event_hash: result.verification_output.event_hash,
hash: [0; 32],
att: Attestation::default(),
prev_block_cert: *ru.att(),
txroot,
faultroot,
iteration,
failed_iterations,
};

Ok(Block::new(blk_header, txs, faults).expect("block should be valid"))
Block::new(blk_header, txs, faults.to_vec()).map_err(|e| {
crate::operations::Error::InvalidEST(anyhow::anyhow!(
"Cannot create new block {e}",
))
})
}
}
12 changes: 11 additions & 1 deletion consensus/src/proposal/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
// 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::config::{
MAX_BLOCK_SIZE, MAX_NUMBER_OF_FAULTS, MAX_NUMBER_OF_TRANSACTIONS,
};
use crate::merkle::merkle_root;
use crate::msg_handler::{HandleMsgOutput, MsgHandler};
use crate::user::committee::Committee;
Expand Down Expand Up @@ -89,6 +91,14 @@ impl<D: Database> ProposalHandler<D> {
return Err(ConsensusError::NotCommitteeMember);
}

let candidate_size = p
.candidate
.size()
.map_err(|_| ConsensusError::UnknownBlockSize)?;
if candidate_size > MAX_BLOCK_SIZE {
return Err(ConsensusError::InvalidBlockSize(candidate_size));
}

// Verify new_block msg signature
p.verify_signature()?;

Expand Down
2 changes: 2 additions & 0 deletions node-data/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,15 @@ impl Serializable for Transaction {
let tx_type = Self::read_u32_le(r)?;

let protocol_tx = Self::read_var_le_bytes32(r)?;
let tx_size = protocol_tx.len();
let inner = ProtocolTransaction::from_slice(&protocol_tx[..])
.map_err(|_| io::Error::from(io::ErrorKind::InvalidData))?;

Ok(Self {
inner,
version,
r#type: tx_type,
size: Some(tx_size),
})
}
}
Expand Down
2 changes: 1 addition & 1 deletion node-data/src/ledger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ mod header;
pub use header::{Header, Seed};

mod block;
pub use block::{Block, BlockWithLabel, Hash, Label};
pub use block::*;

mod transaction;
pub use transaction::{SpendingId, SpentTransaction, Transaction};
Expand Down
6 changes: 6 additions & 0 deletions node-data/src/ledger/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ impl Block {
pub fn set_attestation(&mut self, att: Attestation) {
self.header.att = att;
}

pub fn size(&self) -> io::Result<usize> {
let mut buf = vec![];
self.write(&mut buf)?;
Ok(buf.len())
}
}

#[derive(Debug, Clone, Copy, PartialEq)]
Expand Down
22 changes: 22 additions & 0 deletions node-data/src/ledger/faults.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,28 @@ pub enum Fault {
DoubleValidationVote(FaultData<Vote>, FaultData<Vote>),
}

impl Fault {
pub fn size(&self) -> usize {
// prev_block_hash + round + iter
const FAULT_CONSENSUS_HEADER_SIZE: usize = 32 + u64::SIZE + u8::SIZE;
// signer + signature
const FAULT_SIG_INFO_SIZE: usize =
BlsMultisigPublicKey::SIZE + BlsMultisigSignature::SIZE;

const HEADERS: usize = FAULT_CONSENSUS_HEADER_SIZE * 2;
const SIG_INFOS: usize = FAULT_SIG_INFO_SIZE * 2;
let faults_data_size = match self {
Fault::DoubleCandidate(..) => 32 * 2,
Fault::DoubleRatificationVote(a, b) => {
a.data.size() + b.data.size()
}
Fault::DoubleValidationVote(a, b) => a.data.size() + b.data.size(),
};

HEADERS + SIG_INFOS + faults_data_size
}
}

#[derive(Debug, Error)]
pub enum InvalidFault {
#[error("Inner faults have same data")]
Expand Down
8 changes: 8 additions & 0 deletions node-data/src/ledger/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ pub struct Header {
pub att: Attestation,
}

impl Header {
pub fn size(&self) -> io::Result<usize> {
let mut buf = vec![];
self.write(&mut buf)?;
Ok(buf.len())
}
}

impl std::fmt::Debug for Header {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let timestamp =
Expand Down
21 changes: 20 additions & 1 deletion node-data/src/ledger/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,35 @@
//
// Copyright (c) DUSK NETWORK. All rights reserved.

use dusk_bytes::Serializable;
use std::io;

use dusk_bytes::Serializable as DuskSerializable;
use execution_core::signatures::bls;
use execution_core::transfer::Transaction as ProtocolTransaction;
use execution_core::BlsScalar;
use serde::Serialize;

use crate::Serializable;

#[derive(Debug, Clone)]
pub struct Transaction {
pub version: u32,
pub r#type: u32,
pub inner: ProtocolTransaction,
pub(crate) size: Option<usize>,
}

impl Transaction {
pub fn size(&self) -> io::Result<usize> {
match self.size {
Some(size) => Ok(size),
None => {
let mut buf = vec![];
self.write(&mut buf)?;
Ok(buf.len())
}
}
}
}

impl From<ProtocolTransaction> for Transaction {
Expand All @@ -23,6 +41,7 @@ impl From<ProtocolTransaction> for Transaction {
inner: value,
r#type: 1,
version: 1,
size: None,
}
}
}
Expand Down
14 changes: 14 additions & 0 deletions node-data/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,20 @@ pub mod payload {
NoQuorum = 3,
}

impl Vote {
pub fn size(&self) -> usize {
const ENUM_BYTE: usize = 1;

let data_size: usize = match &self {
Vote::NoCandidate => 0,
Vote::Valid(_) => 32,
Vote::Invalid(_) => 32,
Vote::NoQuorum => 0,
};
ENUM_BYTE + data_size
}
}

impl fmt::Debug for Vote {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (desc, hash) = match &self {
Expand Down
15 changes: 8 additions & 7 deletions node/src/chain/header_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::database;
use crate::database::Ledger;
use anyhow::anyhow;
use dusk_bytes::Serializable;
use dusk_consensus::config::MINIMUM_BLOCK_TIME;
use dusk_consensus::config::{MINIMUM_BLOCK_TIME, RELAX_ITERATION_THRESHOLD};
use dusk_consensus::operations::Voter;
use dusk_consensus::quorum::verifiers;
use dusk_consensus::quorum::verifiers::QuorumResult;
Expand Down Expand Up @@ -205,12 +205,13 @@ impl<'a, DB: database::DB> Validator<'a, DB> {
) -> anyhow::Result<u8> {
let mut failed_atts = 0u8;

for (iter, att) in candidate_block
.failed_iterations
.att_list
.iter()
.enumerate()
{
let att_list = &candidate_block.failed_iterations.att_list;

if att_list.len() > RELAX_ITERATION_THRESHOLD as usize {
anyhow::bail!("Too many failed iterations {}", att_list.len())
}

for (iter, att) in att_list.iter().enumerate() {
if let Some((att, pk)) = att {
info!(event = "verify_att", att_type = "failed_att", iter);

Expand Down
12 changes: 2 additions & 10 deletions rusk/benches/block_ingestion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,7 @@ fn load_phoenix_txs() -> Vec<Transaction> {
let line = line.unwrap();
let tx_bytes = hex::decode(line).unwrap();
let tx = ProtocolTransaction::from_slice(&tx_bytes).unwrap();
txs.push(Transaction {
version: 1,
r#type: 0,
inner: tx,
});
txs.push(tx.into());
}

preverify(&txs);
Expand All @@ -65,11 +61,7 @@ fn load_moonlight_txs() -> Vec<Transaction> {
let line = line.unwrap();
let tx_bytes = hex::decode(line).unwrap();
let tx = ProtocolTransaction::from_slice(&tx_bytes).unwrap();
txs.push(Transaction {
version: 1,
r#type: 0,
inner: tx,
});
txs.push(tx.into());
}

preverify(&txs);
Expand Down
Loading

0 comments on commit 1eb38b7

Please sign in to comment.