Skip to content

Commit

Permalink
Use packable struct-level verify-with (#1609)
Browse files Browse the repository at this point in the history
* Use packable struct-level verify-with

* Derive packable for ManaAllotment

* Derive packable for MultiAddress

* Derive packable for SimpleTokenScheme

* Derive packable for BasicBlock

* Derive packable for Block

* Derive packable for Payload

* Use newly released packable

* Use InvalidUnlockConditionKind

* Nit

* Missing VERIFY

* Packable verify_protocol_parameters_hash
  • Loading branch information
thibault-martinez authored Nov 16, 2023
1 parent bf705a0 commit 383b8a7
Show file tree
Hide file tree
Showing 14 changed files with 310 additions and 634 deletions.
358 changes: 171 additions & 187 deletions Cargo.lock

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions bindings/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,20 @@ iota-crypto = { version = "0.23.0", default-features = false, features = [
"bip44",
] }
log = { version = "0.4.20", default-features = false }
packable = { version = "0.8.3", default-features = false }
packable = { version = "0.9.0", default-features = false }
prefix-hex = { version = "0.7.1", default-features = false }
primitive-types = { version = "0.12.2", default-features = false }
serde = { version = "1.0.188", default-features = false }
serde_json = { version = "1.0.107", default-features = false }
thiserror = { version = "1.0.49", default-features = false }
tokio = { version = "1.33.0", default-features = false }
url = { version = "2.4.1", default-features = false, features = [
"serde",
] }
url = { version = "2.4.1", default-features = false, features = ["serde"] }
zeroize = { version = "1.6.0", default-features = false }

[dev-dependencies]
pretty_assertions = { version = "1.4.0", default-features = false, features = [ "alloc" ] }
pretty_assertions = { version = "1.4.0", default-features = false, features = [
"alloc",
] }

[features]
events = ["iota-sdk/events"]
Expand Down
2 changes: 1 addition & 1 deletion sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ iota-crypto = { version = "0.23.0", default-features = false, features = [
"secp256k1",
] }
iterator-sorted = { version = "0.1.0", default-features = false }
packable = { version = "0.8.3", default-features = false, features = [
packable = { version = "0.9.0", default-features = false, features = [
"primitive-types",
] }
paste = { version = "1.0.14", default-features = false }
Expand Down
66 changes: 15 additions & 51 deletions sdk/src/types/block/address/multi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,7 @@ use core::{fmt, ops::RangeInclusive};

use derive_more::{AsRef, Deref, Display, From};
use iterator_sorted::is_unique_sorted;
use packable::{
bounded::BoundedU8,
error::{UnpackError, UnpackErrorExt},
packer::Packer,
prefix::BoxedSlicePrefix,
unpacker::Unpacker,
Packable,
};
use packable::{bounded::BoundedU8, prefix::BoxedSlicePrefix, Packable};

use crate::types::block::{address::Address, output::StorageScore, Error};

Expand Down Expand Up @@ -77,12 +70,17 @@ fn verify_weight<const VERIFY: bool>(weight: &u8, _visitor: &()) -> Result<(), E
/// An address that consists of addresses with weights and a threshold value.
/// The Multi Address can be unlocked if the cumulative weight of all unlocked addresses is equal to or exceeds the
/// threshold.
#[derive(Clone, Debug, Deref, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Clone, Debug, Deref, Eq, PartialEq, Ord, PartialOrd, Hash, Packable)]
#[packable(unpack_error = Error)]
#[packable(verify_with = verify_multi_address)]
pub struct MultiAddress {
/// The weighted unlocked addresses.
#[deref]
#[packable(verify_with = verify_addresses)]
#[packable(unpack_error_with = |e| e.unwrap_item_err_or_else(|p| Error::InvalidWeightedAddressCount(p.into())))]
addresses: BoxedSlicePrefix<WeightedAddress, WeightedAddressCount>,
/// The threshold that needs to be reached by the unlocked addresses in order to unlock the multi address.
#[packable(verify_with = verify_threshold)]
threshold: u16,
}

Expand All @@ -103,9 +101,11 @@ impl MultiAddress {
let addresses = BoxedSlicePrefix::<WeightedAddress, WeightedAddressCount>::try_from(addresses)
.map_err(Error::InvalidWeightedAddressCount)?;

verify_cumulative_weight::<true>(&addresses, &threshold, &())?;
let multi_address = Self { addresses, threshold };

Ok(Self { addresses, threshold })
verify_multi_address::<true>(&multi_address, &())?;

Ok(multi_address)
}

/// Returns the addresses of a [`MultiAddress`].
Expand All @@ -121,38 +121,6 @@ impl MultiAddress {
}
}

impl Packable for MultiAddress {
type UnpackError = Error;
type UnpackVisitor = ();

#[inline]
fn pack<P: Packer>(&self, packer: &mut P) -> Result<(), P::Error> {
self.addresses.pack(packer)?;
self.threshold.pack(packer)?;

Ok(())
}

#[inline]
fn unpack<U: Unpacker, const VERIFY: bool>(
unpacker: &mut U,
visitor: &Self::UnpackVisitor,
) -> Result<Self, UnpackError<Self::UnpackError, U::Error>> {
let addresses =
BoxedSlicePrefix::<WeightedAddress, WeightedAddressCount>::unpack::<_, VERIFY>(unpacker, visitor)
.map_packable_err(|e| e.unwrap_item_err_or_else(|e| Error::InvalidWeightedAddressCount(e.into())))?;

verify_addresses::<VERIFY>(&addresses, &()).map_err(UnpackError::Packable)?;

let threshold = u16::unpack::<_, VERIFY>(unpacker, visitor).coerce()?;

verify_threshold::<VERIFY>(&threshold, &()).map_err(UnpackError::Packable)?;
verify_cumulative_weight::<VERIFY>(&addresses, &threshold, &()).map_err(UnpackError::Packable)?;

Ok(Self { addresses, threshold })
}
}

fn verify_addresses<const VERIFY: bool>(addresses: &[WeightedAddress], _visitor: &()) -> Result<(), Error> {
if VERIFY && !is_unique_sorted(addresses.iter().map(WeightedAddress::address)) {
return Err(Error::WeightedAddressesNotUniqueSorted);
Expand All @@ -169,18 +137,14 @@ fn verify_threshold<const VERIFY: bool>(threshold: &u16, _visitor: &()) -> Resul
}
}

fn verify_cumulative_weight<const VERIFY: bool>(
addresses: &[WeightedAddress],
threshold: &u16,
_visitor: &(),
) -> Result<(), Error> {
fn verify_multi_address<const VERIFY: bool>(address: &MultiAddress, _visitor: &()) -> Result<(), Error> {
if VERIFY {
let cumulative_weight = addresses.iter().map(|address| address.weight as u16).sum::<u16>();
let cumulative_weight = address.iter().map(|address| address.weight as u16).sum::<u16>();

if cumulative_weight < *threshold {
if cumulative_weight < address.threshold {
return Err(Error::InvalidMultiAddressCumulativeWeight {
cumulative_weight,
threshold: *threshold,
threshold: address.threshold,
});
}
}
Expand Down
57 changes: 13 additions & 44 deletions sdk/src/types/block/core/basic.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
// Copyright 2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use packable::{
error::{UnpackError, UnpackErrorExt},
packer::Packer,
unpacker::Unpacker,
Packable,
};
use packable::Packable;

use crate::types::block::{
core::{parent::verify_parents_sets, Block, Parents},
Expand Down Expand Up @@ -107,7 +102,10 @@ impl From<BasicBlock> for BasicBlockBuilder {
}
}

#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq, Packable)]
#[packable(unpack_error = Error)]
#[packable(unpack_visitor = ProtocolParameters)]
#[packable(verify_with = verify_basic_block)]
pub struct BasicBlock {
/// Blocks that are strongly directly approved.
strong_parents: StrongParents,
Expand Down Expand Up @@ -156,45 +154,16 @@ impl BasicBlock {
}
}

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

fn pack<P: Packer>(&self, packer: &mut P) -> Result<(), P::Error> {
self.strong_parents.pack(packer)?;
self.weak_parents.pack(packer)?;
self.shallow_like_parents.pack(packer)?;
self.payload.pack(packer)?;
self.max_burned_mana.pack(packer)?;

Ok(())
fn verify_basic_block<const VERIFY: bool>(basic_block: &BasicBlock, _: &ProtocolParameters) -> Result<(), Error> {
if VERIFY {
verify_parents_sets(
&basic_block.strong_parents,
&basic_block.weak_parents,
&basic_block.shallow_like_parents,
)?;
}

fn unpack<U: Unpacker, const VERIFY: bool>(
unpacker: &mut U,
visitor: &Self::UnpackVisitor,
) -> Result<Self, UnpackError<Self::UnpackError, U::Error>> {
let strong_parents = StrongParents::unpack::<_, VERIFY>(unpacker, &())?;
let weak_parents = WeakParents::unpack::<_, VERIFY>(unpacker, &())?;
let shallow_like_parents = ShallowLikeParents::unpack::<_, VERIFY>(unpacker, &())?;

if VERIFY {
verify_parents_sets(&strong_parents, &weak_parents, &shallow_like_parents)
.map_err(UnpackError::Packable)?;
}

let payload = OptionalPayload::unpack::<_, VERIFY>(unpacker, visitor)?;

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

Ok(Self {
strong_parents,
weak_parents,
shallow_like_parents,
payload,
max_burned_mana,
})
}
Ok(())
}

#[cfg(feature = "serde")]
Expand Down
45 changes: 7 additions & 38 deletions sdk/src/types/block/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,7 @@ use alloc::boxed::Box;

use crypto::hashes::{blake2b::Blake2b256, Digest};
use derive_more::From;
use packable::{
error::{UnpackError, UnpackErrorExt},
packer::Packer,
unpacker::Unpacker,
Packable, PackableExt,
};
use packable::{Packable, PackableExt};

pub use self::{
basic::{BasicBlock, BasicBlockBuilder},
Expand All @@ -28,9 +23,14 @@ use crate::types::block::{
Error,
};

#[derive(Clone, Debug, Eq, PartialEq, From)]
#[derive(Clone, Debug, Eq, PartialEq, From, Packable)]
#[packable(unpack_error = Error)]
#[packable(unpack_visitor = ProtocolParameters)]
#[packable(tag_type = u8, with_error = Error::InvalidBlockKind)]
pub enum Block {
#[packable(tag = BasicBlock::KIND)]
Basic(Box<BasicBlock>),
#[packable(tag = ValidationBlock::KIND)]
Validation(Box<ValidationBlock>),
}

Expand Down Expand Up @@ -102,37 +102,6 @@ impl Block {
}
}

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

fn pack<P: Packer>(&self, packer: &mut P) -> Result<(), P::Error> {
match self {
Self::Basic(block) => {
BasicBlock::KIND.pack(packer)?;
block.pack(packer)
}
Self::Validation(block) => {
ValidationBlock::KIND.pack(packer)?;
block.pack(packer)
}
}?;

Ok(())
}

fn unpack<U: Unpacker, const VERIFY: bool>(
unpacker: &mut U,
visitor: &Self::UnpackVisitor,
) -> Result<Self, UnpackError<Self::UnpackError, U::Error>> {
Ok(match u8::unpack::<_, VERIFY>(unpacker, &()).coerce()? {
BasicBlock::KIND => Self::from(BasicBlock::unpack::<_, VERIFY>(unpacker, visitor).coerce()?),
ValidationBlock::KIND => Self::from(ValidationBlock::unpack::<_, VERIFY>(unpacker, visitor).coerce()?),
k => return Err(UnpackError::Packable(Error::InvalidBlockKind(k))),
})
}
}

#[cfg(feature = "serde")]
pub(crate) mod dto {
use alloc::format;
Expand Down
Loading

0 comments on commit 383b8a7

Please sign in to comment.