Skip to content

Commit

Permalink
Add BlockHeader and replace protocol params with id in wrapper (#1264)
Browse files Browse the repository at this point in the history
* Add BlockHeader and replace protocol params with id in wrapper

* spacing

Co-authored-by: Thibault Martinez <[email protected]>

* error string

Co-authored-by: Thibault Martinez <[email protected]>

* move id

* fix LENGTH

---------

Co-authored-by: Thibault Martinez <[email protected]>
  • Loading branch information
DaughterOfMars and thibault-martinez authored Sep 20, 2023
1 parent a37e9e9 commit 916549c
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 127 deletions.
2 changes: 1 addition & 1 deletion sdk/src/client/api/block_builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ impl ClientInner {
});

Ok(BlockWrapper::new(
self.get_protocol_parameters().await?,
&self.get_protocol_parameters().await?,
issuing_time,
commitment.id(),
latest_finalized_slot,
Expand Down
256 changes: 136 additions & 120 deletions sdk/src/types/block/core/wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use core::mem::size_of;

use crypto::hashes::{blake2b::Blake2b256, Digest};
use getset::{CopyGetters, Getters};
use packable::{
error::{UnexpectedEOF, UnpackError, UnpackErrorExt},
packer::{Packer, SlicePacker},
Expand All @@ -20,12 +21,13 @@ use crate::types::block::{
Block, Error, IssuerId,
};

/// Represent the object that nodes gossip around the network.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct BlockWrapper {
/// Protocol parameters of the network to which this block belongs.
// TODO Not fully sure this is worth the small UX improvement, needs to be discussed more.
protocol_params: ProtocolParameters,
#[derive(Clone, Debug, Eq, PartialEq, CopyGetters)]
#[getset(get_copy = "pub")]
pub struct BlockHeader {
/// Protocol version of the network to which this block belongs.
protocol_version: u8,
/// The identifier of the network to which this block belongs.
network_id: u64,
/// The time at which the block was issued. It is a Unix timestamp in nanoseconds.
issuing_time: u64,
/// The identifier of the slot to which this block commits.
Expand All @@ -34,133 +36,174 @@ pub struct BlockWrapper {
latest_finalized_slot: SlotIndex,
/// The identifier of the account that issued this block.
issuer_id: IssuerId,
}

impl BlockHeader {
/// The length of the block header.
pub const LENGTH: usize =
size_of::<u8>() + 2 * size_of::<u64>() + SlotCommitmentId::LENGTH + size_of::<SlotIndex>() + IssuerId::LENGTH;

pub(crate) fn hash(&self) -> [u8; 32] {
let mut bytes = [0u8; Self::LENGTH];

self.pack(&mut SlicePacker::new(&mut bytes)).unwrap();
Blake2b256::digest(bytes).into()
}
}

impl Packable for BlockHeader {
type UnpackError = Error;
type UnpackVisitor = ProtocolParameters;

fn pack<P: Packer>(&self, packer: &mut P) -> Result<(), P::Error> {
self.protocol_version.pack(packer)?;
self.network_id.pack(packer)?;
self.issuing_time.pack(packer)?;
self.slot_commitment_id.pack(packer)?;
self.latest_finalized_slot.pack(packer)?;
self.issuer_id.pack(packer)?;

Ok(())
}

fn unpack<U: Unpacker, const VERIFY: bool>(
unpacker: &mut U,
protocol_params: &Self::UnpackVisitor,
) -> Result<Self, UnpackError<Self::UnpackError, U::Error>> {
let protocol_version = u8::unpack::<_, VERIFY>(unpacker, &()).coerce()?;

if VERIFY && protocol_version != protocol_params.version() {
return Err(UnpackError::Packable(Error::ProtocolVersionMismatch {
expected: protocol_params.version(),
actual: protocol_version,
}));
}

let network_id = u64::unpack::<_, VERIFY>(unpacker, &()).coerce()?;

if VERIFY && network_id != protocol_params.network_id() {
return Err(UnpackError::Packable(Error::NetworkIdMismatch {
expected: protocol_params.network_id(),
actual: network_id,
}));
}

let issuing_time = u64::unpack::<_, VERIFY>(unpacker, &()).coerce()?;

let slot_commitment_id = SlotCommitmentId::unpack::<_, VERIFY>(unpacker, &()).coerce()?;

let latest_finalized_slot = SlotIndex::unpack::<_, VERIFY>(unpacker, &()).coerce()?;

let issuer_id = IssuerId::unpack::<_, VERIFY>(unpacker, &()).coerce()?;

Ok(Self {
protocol_version,
network_id,
issuing_time,
slot_commitment_id,
latest_finalized_slot,
issuer_id,
})
}
}

/// Represent the object that nodes gossip around the network.
#[derive(Clone, Debug, Eq, PartialEq, Getters, CopyGetters)]
pub struct BlockWrapper {
#[getset(skip)]
header: BlockHeader,
/// The inner block.
#[getset(get = "pub")]
block: Block,
/// The block signature, used to validate issuance capabilities.
#[getset(get_copy = "pub")]
signature: Signature,
/// The identifier of the block.
#[getset(get_copy = "pub")]
id: BlockId,
}

impl BlockWrapper {
/// The minimum number of bytes in a block.
pub const LENGTH_MIN: usize = 46;
/// The maximum number of bytes in a block.
pub const LENGTH_MAX: usize = 32768;
/// The length of the block header.
pub const HEADER_LENGTH: usize = size_of::<u8>()
+ 2 * size_of::<u64>()
+ size_of::<SlotCommitmentId>()
+ size_of::<SlotIndex>()
+ size_of::<IssuerId>();

/// Creates a new [`BlockWrapper`].
#[inline(always)]
pub fn new(
protocol_params: ProtocolParameters,
protocol_params: &ProtocolParameters,
issuing_time: u64,
slot_commitment_id: SlotCommitmentId,
latest_finalized_slot: SlotIndex,
issuer_id: IssuerId,
block: impl Into<Block>,
signature: impl Into<Signature>,
) -> Self {
Self {
protocol_params,
let block = block.into();
let signature = signature.into();
let header = BlockHeader {
protocol_version: protocol_params.version(),
network_id: protocol_params.network_id(),
issuing_time,
slot_commitment_id,
latest_finalized_slot,
issuer_id,
block: block.into(),
signature: signature.into(),
};
Self {
id: Self::block_id(&header, &block, &signature, protocol_params),
header,
block,
signature,
}
}

/// Returns the protocol version of a [`BlockWrapper`].
#[inline(always)]
pub fn protocol_version(&self) -> u8 {
self.protocol_params.version()
}

/// Returns the protocol parameters of a [`BlockWrapper`].
#[inline(always)]
pub fn protocol_parameters(&self) -> &ProtocolParameters {
&self.protocol_params
self.header.protocol_version()
}

/// Returns the network id of a [`BlockWrapper`].
#[inline(always)]
pub fn network_id(&self) -> u64 {
self.protocol_params.network_id()
self.header.network_id()
}

/// Returns the issuing time of a [`BlockWrapper`].
#[inline(always)]
pub fn issuing_time(&self) -> u64 {
self.issuing_time
self.header.issuing_time()
}

/// Returns the slot commitment ID of a [`BlockWrapper`].
#[inline(always)]
pub fn slot_commitment_id(&self) -> SlotCommitmentId {
self.slot_commitment_id
self.header.slot_commitment_id()
}

/// Returns the latest finalized slot of a [`BlockWrapper`].
#[inline(always)]
pub fn latest_finalized_slot(&self) -> SlotIndex {
self.latest_finalized_slot
self.header.latest_finalized_slot()
}

/// Returns the issuer ID of a [`BlockWrapper`].
#[inline(always)]
pub fn issuer_id(&self) -> IssuerId {
self.issuer_id
}

/// Returns the [`Block`] of a [`BlockWrapper`].
#[inline(always)]
pub fn block(&self) -> &Block {
&self.block
}

/// Returns the signature of a [`BlockWrapper`].
#[inline(always)]
pub fn signature(&self) -> &Signature {
&self.signature
}

pub(crate) fn pack_header<P: Packer>(&self, packer: &mut P) -> Result<(), P::Error> {
self.protocol_version().pack(packer)?;
self.network_id().pack(packer)?;
self.issuing_time.pack(packer)?;
self.slot_commitment_id.pack(packer)?;
self.latest_finalized_slot.pack(packer)?;
self.issuer_id.pack(packer)?;

Ok(())
}

pub(crate) fn header_hash(&self) -> [u8; 32] {
let mut bytes = [0u8; Self::HEADER_LENGTH];

self.pack_header(&mut SlicePacker::new(&mut bytes)).unwrap();
Blake2b256::digest(bytes).into()
}

/// Computes the block hash.
pub fn hash(&self) -> BlockHash {
let id = [
&self.header_hash()[..],
&self.block.hash()[..],
&self.signature.pack_to_vec(),
]
.concat();
BlockHash::new(Blake2b256::digest(id).into())
self.header.issuer_id()
}

/// Computes the block identifier.
pub fn id(&self) -> BlockId {
self.hash()
.with_slot_index(self.protocol_parameters().slot_index(self.issuing_time()))
pub fn block_id(
block_header: &BlockHeader,
block: &Block,
signature: &Signature,
protocol_params: &ProtocolParameters,
) -> BlockId {
let id = [&block_header.hash()[..], &block.hash()[..], &signature.pack_to_vec()].concat();
let block_hash = BlockHash::new(Blake2b256::digest(id).into());
block_hash.with_slot_index(protocol_params.slot_index(block_header.issuing_time()))
}

/// Unpacks a [`BlockWrapper`] from a sequence of bytes doing syntactical checks and verifying that
Expand Down Expand Up @@ -208,7 +251,7 @@ impl Packable for BlockWrapper {
type UnpackVisitor = ProtocolParameters;

fn pack<P: Packer>(&self, packer: &mut P) -> Result<(), P::Error> {
self.pack_header(packer)?;
self.header.pack(packer)?;
self.block.pack(packer)?;
self.signature.pack(packer)?;

Expand All @@ -221,59 +264,32 @@ impl Packable for BlockWrapper {
) -> Result<Self, UnpackError<Self::UnpackError, U::Error>> {
let start_opt = unpacker.read_bytes();

let protocol_version = u8::unpack::<_, VERIFY>(unpacker, &()).coerce()?;

if VERIFY && protocol_version != protocol_params.version() {
return Err(UnpackError::Packable(Error::ProtocolVersionMismatch {
expected: protocol_params.version(),
actual: protocol_version,
}));
}

let network_id = u64::unpack::<_, VERIFY>(unpacker, &()).coerce()?;

if VERIFY && network_id != protocol_params.network_id() {
return Err(UnpackError::Packable(Error::NetworkIdMismatch {
expected: protocol_params.network_id(),
actual: network_id,
}));
}

let issuing_time = u64::unpack::<_, VERIFY>(unpacker, &()).coerce()?;

let slot_commitment_id = SlotCommitmentId::unpack::<_, VERIFY>(unpacker, &()).coerce()?;

let latest_finalized_slot = SlotIndex::unpack::<_, VERIFY>(unpacker, &()).coerce()?;

let issuer_id = IssuerId::unpack::<_, VERIFY>(unpacker, &()).coerce()?;
let header = BlockHeader::unpack::<_, VERIFY>(unpacker, protocol_params)?;

let block = Block::unpack::<_, VERIFY>(unpacker, protocol_params)?;

let signature = Signature::unpack::<_, VERIFY>(unpacker, &())?;

let wrapper = Self {
id: Self::block_id(&header, &block, &signature, protocol_params),
header,
block,
signature,
};

if VERIFY {
let block_len = if let (Some(start), Some(end)) = (start_opt, unpacker.read_bytes()) {
let wrapper_len = if let (Some(start), Some(end)) = (start_opt, unpacker.read_bytes()) {
end - start
} else {
block.packed_len()
wrapper.packed_len()
};

if block_len > Self::LENGTH_MAX {
return Err(UnpackError::Packable(Error::InvalidBlockLength(block_len)));
if wrapper_len > Self::LENGTH_MAX {
return Err(UnpackError::Packable(Error::InvalidBlockWrapperLength(wrapper_len)));
}
}

let block_wrapper = Self {
protocol_params: protocol_params.clone(),
issuing_time,
slot_commitment_id,
latest_finalized_slot,
issuer_id,
block,
signature,
};

Ok(block_wrapper)
Ok(wrapper)
}
}

Expand Down Expand Up @@ -308,10 +324,10 @@ pub(crate) mod dto {
Self {
protocol_version: value.protocol_version(),
network_id: value.network_id(),
issuing_time: value.issuing_time,
slot_commitment_id: value.slot_commitment_id,
latest_finalized_slot: value.latest_finalized_slot,
issuer_id: value.issuer_id,
issuing_time: value.issuing_time(),
slot_commitment_id: value.slot_commitment_id(),
latest_finalized_slot: value.latest_finalized_slot(),
issuer_id: value.issuer_id(),
block: BlockDto::from(&value.block),
signature: value.signature,
}
Expand All @@ -335,12 +351,12 @@ pub(crate) mod dto {
}

Ok(BlockWrapper::new(
protocol_params.clone(),
&protocol_params,
dto.issuing_time,
dto.slot_commitment_id,
dto.latest_finalized_slot,
dto.issuer_id,
Block::try_from_dto_with_params(dto.block, protocol_params)?,
Block::try_from_dto_with_params(dto.block, &protocol_params)?,
dto.signature,
))
}
Expand Down
Loading

0 comments on commit 916549c

Please sign in to comment.