Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

consensus: check block size #2302

Merged
merged 4 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()
fed-franz marked this conversation as resolved.
Show resolved Hide resolved
}
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,
};
fed-franz marked this conversation as resolved.
Show resolved Hide resolved
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
Loading