Skip to content

Commit

Permalink
Verify block slot (#2055)
Browse files Browse the repository at this point in the history
* Make Block::new fallible

* impl verify_block_slot

* finish_with_params

* errors

* Remove comments

* Update sdk/src/types/block/error.rs

Co-authored-by: Thoralf-M <[email protected]>

---------

Co-authored-by: Thoralf-M <[email protected]>
  • Loading branch information
thibault-martinez and Thoralf-M authored Feb 26, 2024
1 parent 0eb252e commit 585260e
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 17 deletions.
72 changes: 55 additions & 17 deletions sdk/src/types/block/core/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::types::block::{
block_id::{BlockHash, BlockId},
core::{BasicBlockBody, ValidationBlockBody},
output::AccountId,
payload::Payload,
protocol::ProtocolParameters,
signature::Signature,
slot::{SlotCommitmentId, SlotIndex},
Expand Down Expand Up @@ -56,8 +57,26 @@ impl UnsignedBlock {
[self.header.hash(), self.body.hash()].concat()
}

/// Finishes an [`UnsignedBlock`] into a [`Block`].
pub fn finish_with_params<'a>(
self,
signature: impl Into<Signature>,
params: impl Into<Option<&'a ProtocolParameters>>,
) -> Result<Block, Error> {
if let Some(params) = params.into() {
verify_block_slot(&self.header, &self.body, params)?;
}

Ok(Block {
header: self.header,
body: self.body,
signature: signature.into(),
})
}

/// Finishes an [`UnsignedBlock`] into a [`Block`] without protocol validation.
pub fn finish(self, signature: impl Into<Signature>) -> Result<Block, Error> {
Ok(Block::new(self.header, self.body, signature))
self.finish_with_params(signature, None)
}
}

Expand Down Expand Up @@ -154,18 +173,6 @@ impl Block {
/// The maximum number of bytes in a block.
pub const LENGTH_MAX: usize = 32768;

/// Creates a new [`Block`].
#[inline(always)]
pub fn new(header: BlockHeader, body: BlockBody, signature: impl Into<Signature>) -> Self {
let signature = signature.into();

Self {
header,
body,
signature,
}
}

/// Creates a new [`UnsignedBlock`].
#[inline(always)]
pub fn build(header: BlockHeader, body: BlockBody) -> UnsignedBlock {
Expand Down Expand Up @@ -288,7 +295,9 @@ impl Packable for Block {
signature,
};

if protocol_params.is_some() {
if let Some(protocol_params) = protocol_params {
verify_block_slot(&block.header, &block.body, &protocol_params).map_err(UnpackError::Packable)?;

let block_len = if let (Some(start), Some(end)) = (start_opt, unpacker.read_bytes()) {
end - start
} else {
Expand All @@ -304,6 +313,35 @@ impl Packable for Block {
}
}

fn verify_block_slot(header: &BlockHeader, body: &BlockBody, params: &ProtocolParameters) -> Result<(), Error> {
if let BlockBody::Basic(basic) = body {
if let Some(Payload::SignedTransaction(signed_transaction)) = basic.payload() {
let transaction = signed_transaction.transaction();
let block_slot = params.slot_index(header.issuing_time / 1_000_000_000);

if block_slot < transaction.creation_slot() {
return Err(Error::BlockSlotBeforeTransactionCreationSlot);
}

if let Some(commitment) = signed_transaction.transaction().context_inputs().commitment() {
let commitment_slot = commitment.slot_index();

if !(block_slot - params.max_committable_age()..=block_slot - params.min_committable_age())
.contains(&commitment_slot)
{
return Err(Error::TransactionCommitmentSlotNotInBlockSlotInterval);
}

if commitment_slot > header.slot_commitment_id.slot_index() {
return Err(Error::TransactionCommitmentSlotAfterBlockCommitmentSlot);
}
}
}
}

Ok(())
}

#[cfg(feature = "serde")]
pub(crate) mod dto {
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -366,11 +404,11 @@ pub(crate) mod dto {
}
}

Ok(Self::new(
UnsignedBlock::new(
BlockHeader::try_from_dto_with_params_inner(dto.inner.header, params)?,
BlockBody::try_from_dto_with_params_inner(dto.inner.body, params)?,
dto.signature,
))
)
.finish_with_params(dto.signature, params)
}
}

Expand Down
13 changes: 13 additions & 0 deletions sdk/src/types/block/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@ pub enum Error {
},
TrailingCapabilityBytes,
RestrictedAddressCapability(AddressCapabilityFlag),
BlockSlotBeforeTransactionCreationSlot,
TransactionCommitmentSlotNotInBlockSlotInterval,
TransactionCommitmentSlotAfterBlockCommitmentSlot,
}

#[cfg(feature = "std")]
Expand Down Expand Up @@ -450,6 +453,16 @@ impl fmt::Display for Error {
}
Self::TrailingCapabilityBytes => write!(f, "capability bytes have trailing zeroes"),
Self::RestrictedAddressCapability(cap) => write!(f, "restricted address capability: {cap:?}"),
Self::BlockSlotBeforeTransactionCreationSlot => {
write!(f, "the block slot is before its contained transaction creation slot")
}
Self::TransactionCommitmentSlotNotInBlockSlotInterval => write!(
f,
"the transaction commitment slot is not in the allowed block slot interval"
),
Self::TransactionCommitmentSlotAfterBlockCommitmentSlot => {
write!(f, "the transaction commitment slot is after the block commitment slot")
}
}
}
}
Expand Down

0 comments on commit 585260e

Please sign in to comment.