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

Add BlockHeader and replace protocol params with id in wrapper #1264

Merged
merged 9 commits into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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: 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
255 changes: 135 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,9 +36,89 @@ 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::<Self>();

pub(crate) fn hash(&self) -> [u8; 32] {
Thoralf-M marked this conversation as resolved.
Show resolved Hide resolved
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 {
/// The identifier of the block.
#[getset(get_copy = "pub")]
id: BlockId,
thibault-martinez marked this conversation as resolved.
Show resolved Hide resolved
#[getset(skip)]
header: BlockHeader,
/// The inner block.
#[getset(get = "pub")]
block: Block,
/// The block signature, used to validate issuance capabilities.
#[getset(get_copy = "pub")]
thibault-martinez marked this conversation as resolved.
Show resolved Hide resolved
signature: Signature,
}

Expand All @@ -45,122 +127,82 @@ impl BlockWrapper {
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 +250,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 +263,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 +323,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 +350,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
Loading