Skip to content

Commit

Permalink
Start verifying maximum request/response sizes
Browse files Browse the repository at this point in the history
  • Loading branch information
hrxi committed Jun 17, 2024
1 parent 91985e4 commit e979a55
Show file tree
Hide file tree
Showing 11 changed files with 62 additions and 25 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions consensus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ nimiq-bls = { workspace = true }
nimiq-hash = { workspace = true }
nimiq-keys = { workspace = true }
nimiq-light-blockchain = { workspace = true }
nimiq-macros = { workspace = true }
nimiq-mmr = { workspace = true }
nimiq-network-interface = { workspace = true }
nimiq-primitives = { workspace = true, features = ["policy", "trie"] }
Expand Down
19 changes: 13 additions & 6 deletions consensus/src/messages/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ use nimiq_blockchain::HistoryTreeChunk;
use nimiq_blockchain_interface::Direction;
use nimiq_hash::Blake2bHash;
use nimiq_keys::Address;
use nimiq_macros::test_max_req_size;
use nimiq_mmr::mmr::proof::SizeProof;
use nimiq_network_interface::{
network::Topic,
request::{RequestCommon, RequestMarker},
};
use nimiq_primitives::{key_nibbles::KeyNibbles, trie::trie_proof::TrieProof};
use nimiq_serde::{Deserialize, Serialize, SerializedMaxSize};
use nimiq_transaction::{
historic_transaction::HistoricTransaction, history_proof::HistoryTreeProof,
};
use serde::{Deserialize, Serialize};
use thiserror::Error;

use crate::error::SubscribeToAddressesError;
Expand Down Expand Up @@ -76,7 +77,7 @@ pub struct MacroChain {
}

/// Error response to [`RequestMacroChain`]
#[derive(Clone, Debug, Deserialize, Error, Serialize)]
#[derive(Clone, Debug, Deserialize, Error, Serialize, SerializedMaxSize)]
pub enum MacroChainError {
/// The locators of the request could not be found.
#[error("unknown locators")]
Expand Down Expand Up @@ -228,7 +229,7 @@ pub enum HistoryChunkError {
/// Request a specific block by hash.
///
/// Can optionally omit the body if it's a micro block.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Deserialize, Serialize, SerializedMaxSize)]
pub struct RequestBlock {
/// The hash of the block that is requested.
pub hash: Blake2bHash,
Expand All @@ -240,7 +241,7 @@ pub struct RequestBlock {
}

/// Error response for [`RequestBlock`].
#[derive(Clone, Debug, Deserialize, Error, Serialize)]
#[derive(Clone, Debug, Deserialize, Error, Serialize, SerializedMaxSize)]
pub enum BlockError {
/// Block hash unknown to the responder.
#[error("target hash not found")]
Expand All @@ -257,6 +258,11 @@ impl RequestCommon for RequestBlock {
type Response = Result<Block, BlockError>;
const MAX_REQUESTS: u32 = MAX_REQUEST_RESPONSE_BLOCK;
}
test_max_req_size!(
RequestBlock,
request_block_req_size,
request_block_resp_size,
);

/// Response to [`RequestMissingBlocks`].
#[derive(Clone, Deserialize, Serialize)]
Expand Down Expand Up @@ -342,10 +348,10 @@ impl RequestCommon for RequestMissingBlocks {
}

/// Request the current blockchain head block hash.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Deserialize, Serialize, SerializedMaxSize)]
pub struct RequestHead {}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Deserialize, Serialize, SerializedMaxSize)]
pub struct ResponseHead {
pub micro: Blake2bHash,
pub r#macro: Blake2bHash,
Expand All @@ -358,6 +364,7 @@ impl RequestCommon for RequestHead {
type Response = ResponseHead;
const MAX_REQUESTS: u32 = MAX_REQUEST_RESPONSE_HEAD;
}
test_max_req_size!(RequestHead, request_head_req_size, request_head_resp_size);

#[derive(Serialize, Deserialize)]
pub struct ResponseTransactionsProof {
Expand Down
22 changes: 22 additions & 0 deletions macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,25 @@ macro_rules! upgrade_weak {
}
};
}

#[macro_export]
macro_rules! test_max_req_size {
($name: ident, $test_name_req: ident, $test_name_resp: ident $(,)?) => {
#[test]
fn $test_name_req() {
use ::nimiq_macros::nimiq_serde::SerializedMaxSize;
use ::nimiq_network_interface::network::MIN_SUPPORTED_REQ_SIZE;
assert!(dbg!($name::MAX_SIZE) <= dbg!(MIN_SUPPORTED_REQ_SIZE));
}
#[test]
fn $test_name_resp() {
use ::nimiq_macros::nimiq_serde::SerializedMaxSize;
use ::nimiq_network_interface::{
network::MIN_SUPPORTED_RESP_SIZE, request::RequestCommon,
};
assert!(
dbg!(<$name as RequestCommon>::Response::MAX_SIZE) <= dbg!(MIN_SUPPORTED_RESP_SIZE)
);
}
};
}
5 changes: 5 additions & 0 deletions network-interface/src/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ pub trait PubsubId<PeerId>: Clone + Send + Sync + Debug {
fn propagation_source(&self) -> PeerId;
}

/// Network implementations have to at least support requests of this size.
pub const MIN_SUPPORTED_REQ_SIZE: usize = 20 * 1024;
/// Network implementations have to at least support responses of this size.
pub const MIN_SUPPORTED_RESP_SIZE: usize = 10 * 1024 * 1024;

#[derive(Copy, Clone, Debug)]
/// Reasons for closing a connection
pub enum CloseReason {
Expand Down
11 changes: 5 additions & 6 deletions network-libp2p/src/dispatch/codecs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,16 @@
//! Note that this doesn't actually serialize/deserialize the message content, but
//! only handles reading/writing the message.
use std::io;
use std::{io, mem};

use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use libp2p::{request_response, StreamProtocol};
use nimiq_network_interface::network;

/// Maximum request size in bytes (20 kB)
const MAX_REQUEST_SIZE: u64 = 20 * 1024;
/// Maximum response size in bytes (10 MB)
const MAX_RESPONSE_SIZE: u64 = 10 * 1024 * 1024;
/// Size of a u64
const U64_LENGTH: usize = std::mem::size_of::<u64>();
const U64_LENGTH: usize = mem::size_of::<u64>();
const MAX_REQUEST_SIZE: u64 = network::MIN_SUPPORTED_REQ_SIZE as u64 + U64_LENGTH as u64;
const MAX_RESPONSE_SIZE: u64 = network::MIN_SUPPORTED_RESP_SIZE as u64 + U64_LENGTH as u64;

#[derive(Default, Debug, Clone)]
pub struct MessageCodec;
Expand Down
4 changes: 2 additions & 2 deletions primitives/block/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use nimiq_network_interface::network::Topic;
use nimiq_primitives::{
coin::Coin, networks::NetworkId, policy::Policy, slots_allocation::Validators,
};
use nimiq_serde::{Deserialize, Serialize};
use nimiq_serde::{Deserialize, Serialize, SerializedMaxSize};
use nimiq_transaction::ExecutedTransaction;
use nimiq_vrf::VrfSeed;

Expand Down Expand Up @@ -61,7 +61,7 @@ impl BlockType {

/// The enum representing a block. Blocks can either be Micro blocks or Macro blocks (which includes
/// both checkpoint and election blocks).
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, SerializedMaxSize)]
pub enum Block {
Macro(MacroBlock),
Micro(MicroBlock),
Expand Down
8 changes: 6 additions & 2 deletions primitives/block/src/micro_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::{
};

/// The struct representing a Micro block.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, SerializedMaxSize)]
pub struct MicroBlock {
/// The header, contains some basic information and commitments to the body and the state.
pub header: MicroHeader,
Expand Down Expand Up @@ -147,7 +147,7 @@ impl fmt::Display for MicroBlock {
}

/// Enumeration representing the justification for a Micro block
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, SerializedMaxSize)]
#[serde(rename_all = "camelCase")]
#[repr(u8)]
pub enum MicroJustification {
Expand Down Expand Up @@ -234,6 +234,10 @@ pub struct MicroBody {
pub transactions: Vec<ExecutedTransaction>,
}

impl SerializedMaxSize for MicroBody {
const MAX_SIZE: usize = Policy::MAX_SIZE_MICRO_BODY;
}

impl MicroBody {
/// Obtains the raw transactions contained in this micro block
pub fn get_raw_transactions(&self) -> Vec<Transaction> {
Expand Down
10 changes: 3 additions & 7 deletions primitives/block/src/multisig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,21 @@ use nimiq_bls::AggregateSignature;
use nimiq_collections::bitset::BitSet;
use nimiq_handel::contribution::{AggregatableContribution, ContributionError};
use nimiq_primitives::policy::Policy;
use serde::{Deserialize, Serialize};
use nimiq_serde::{Deserialize, Serialize, SerializedMaxSize};

/*
This does not really belong here, but as there would otherwise be a cyclic dependency it needs to be here for now.
TODO: Move this out of primitives and into validator/aggregation once the messages crate is no longer required.
*/

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Default)]
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize, SerializedMaxSize)]
pub struct MultiSignature {
pub signature: AggregateSignature,
#[serialize_size(bitset_max_elem = Policy::SLOTS as usize)]
pub signers: BitSet,
}

impl MultiSignature {
#[allow(clippy::identity_op)]
pub const MAX_SIZE: usize = 0
+ /*signature*/ AggregateSignature::SIZE
+ /*signers*/ BitSet::max_size(Policy::SLOTS as usize);

pub fn new(signature: AggregateSignature, signers: BitSet) -> Self {
Self { signature, signers }
}
Expand Down
4 changes: 2 additions & 2 deletions primitives/block/src/skip_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use nimiq_hash_derive::SerializeContent;
use nimiq_primitives::{
policy::Policy, slots_allocation::Validators, Message, SignedMessage, PREFIX_SKIP_BLOCK_INFO,
};
use nimiq_serde::{Deserialize, Serialize};
use nimiq_serde::{Deserialize, Serialize, SerializedMaxSize};
use nimiq_vrf::VrfEntropy;

use crate::{MicroBlock, MultiSignature};
Expand Down Expand Up @@ -44,7 +44,7 @@ impl Message for SkipBlockInfo {
const PREFIX: u8 = PREFIX_SKIP_BLOCK_INFO;
}

#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, SerializedMaxSize)]
pub struct SkipBlockProof {
// The aggregated signature of the validator's signatures for the skip block.
pub sig: MultiSignature,
Expand Down
2 changes: 2 additions & 0 deletions serde/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ mod integer_impls {
use super::SerializedSize;
use super::SerializedMaxSize;

impl SerializedSize for bool { const SIZE: usize = 1; }

impl SerializedSize for i8 { const SIZE: usize = 1; }
impl SerializedSize for u8 { const SIZE: usize = 1; }

Expand Down

0 comments on commit e979a55

Please sign in to comment.