Skip to content

Commit

Permalink
Add CommitmentInput and BlockIssuanceCreditInput (#981)
Browse files Browse the repository at this point in the history
* add commitment and block issuance credit inputs

* add tests

* add fn documentation

* improve test

* fix

* fmt

* remove spaces between fields

* imporve order of methods

* improve panic message

* alternate `is_` and `as_` methods

* fix commitment documentation

* improve DTOs

* fix missing import

* fix macro call

* fix macro calls

* Update sdk/src/types/block/context_input/block_issuance_credit.rs

Co-authored-by: Alexandcoats <[email protected]>

---------

Co-authored-by: Alexandcoats <[email protected]>
  • Loading branch information
abdulmth and Alexandcoats committed Aug 3, 2023
1 parent fd5429c commit 0419ade
Show file tree
Hide file tree
Showing 4 changed files with 267 additions and 31 deletions.
65 changes: 65 additions & 0 deletions sdk/src/types/block/context_input/block_issuance_credit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use derive_more::{Display, From};

use crate::types::block::output::AccountId;

/// A Block Issuance Credit (BIC) Input provides the VM with context for the value of
/// the BIC vector of a specific slot.
#[derive(Clone, Copy, Debug, Display, Eq, PartialEq, Hash, Ord, PartialOrd, From, packable::Packable)]
pub struct BlockIssuanceCreditContextInput(AccountId);

impl BlockIssuanceCreditContextInput {
/// The context input kind of a [`BlockIssuanceCreditContextInput`].
pub const KIND: u8 = 1;

/// Creates a new [`BlockIssuanceCreditContextInput`].
pub fn new(account_id: AccountId) -> Self {
Self(account_id)
}

/// Returns the account id of the [`BlockIssuanceCreditContextInput`].
pub fn account_id(&self) -> AccountId {
self.0
}
}

mod dto {
use alloc::format;

use serde::{Deserialize, Serialize};

use super::*;

/// A Block Issuance Credit Input provides the VM with context for the value of
/// the BIC vector of a specific slot.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct BlockIssuanceCreditContextInputDto {
#[serde(rename = "type")]
kind: u8,
account_id: AccountId,
}

impl From<&BlockIssuanceCreditContextInput> for BlockIssuanceCreditContextInputDto {
fn from(value: &BlockIssuanceCreditContextInput) -> Self {
Self {
kind: BlockIssuanceCreditContextInput::KIND,
account_id: value.account_id(),
}
}
}

impl From<BlockIssuanceCreditContextInputDto> for BlockIssuanceCreditContextInput {
fn from(value: BlockIssuanceCreditContextInputDto) -> Self {
Self::new(value.account_id)
}
}

impl_serde_typed_dto!(
BlockIssuanceCreditContextInput,
BlockIssuanceCreditContextInputDto,
"block issuance credit context input"
);
}
62 changes: 62 additions & 0 deletions sdk/src/types/block/context_input/commitment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright 2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use derive_more::{Display, From};

use crate::types::block::slot::SlotCommitmentId;

/// A Commitment Input indicates that the input references a commitment to a certain slot.
#[derive(Clone, Copy, Display, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, From, packable::Packable)]
pub struct CommitmentContextInput(SlotCommitmentId);

impl CommitmentContextInput {
/// The context input kind of a [`CommitmentContextInput`].
pub const KIND: u8 = 0;

/// Creates a new [`CommitmentContextInput`].
pub fn new(commitment_id: SlotCommitmentId) -> Self {
Self(commitment_id)
}

/// Returns the commitment id of the [`CommitmentContextInput`].
pub fn commitment_id(&self) -> SlotCommitmentId {
self.0
}
}

mod dto {
use alloc::format;

use serde::{Deserialize, Serialize};

use super::*;

/// A Commitment Input indicates that the input references a commitment to a certain slot.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct CommitmentContextInputDto {
#[serde(rename = "type")]
kind: u8,
commitment_id: SlotCommitmentId,
}

impl From<&CommitmentContextInput> for CommitmentContextInputDto {
fn from(value: &CommitmentContextInput) -> Self {
Self {
kind: CommitmentContextInput::KIND,
commitment_id: value.commitment_id(),
}
}
}

impl From<CommitmentContextInputDto> for CommitmentContextInput {
fn from(value: CommitmentContextInputDto) -> Self {
Self::new(value.commitment_id)
}
}
impl_serde_typed_dto!(
CommitmentContextInput,
CommitmentContextInputDto,
"commitment context input"
);
}
159 changes: 132 additions & 27 deletions sdk/src/types/block/context_input/mod.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,41 @@
// Copyright 2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

mod block_issuance_credit;
mod commitment;
mod reward;

use derive_more::From;
use derive_more::{Display, From};

pub use self::reward::RewardContextInput;
pub use self::{
block_issuance_credit::BlockIssuanceCreditContextInput, commitment::CommitmentContextInput,
reward::RewardContextInput,
};
use crate::types::block::Error;

/// A Context Input provides additional contextual information for the execution of a transaction, such as for different
/// functionality related to accounts, commitments, or Mana rewards. A Context Input does not need to be unlocked.
#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd, From, packable::Packable)]
#[derive(Clone, Eq, Display, PartialEq, Hash, Ord, PartialOrd, From, packable::Packable)]
#[packable(unpack_error = Error)]
#[packable(tag_type = u8, with_error = Error::InvalidContextInputKind)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(untagged))]
pub enum ContextInput {
/// A [`CommitmentContextInput`].
#[packable(tag = CommitmentContextInput::KIND)]
Commitment(CommitmentContextInput),
/// A [`BlockIssuanceCreditContextInput`].
#[packable(tag = BlockIssuanceCreditContextInput::KIND)]
BlockIssuanceCredit(BlockIssuanceCreditContextInput),
/// A [`RewardContextInput`].
#[packable(tag = RewardContextInput::KIND)]
Reward(RewardContextInput),
// TODO: Commitment Input https://github.com/iotaledger/iota-sdk/issues/901 and Block Issuance Credit Input https://github.com/iotaledger/iota-sdk/issues/906
}

impl core::fmt::Debug for ContextInput {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Commitment(input) => input.fmt(f),
Self::BlockIssuanceCredit(input) => input.fmt(f),
Self::Reward(input) => input.fmt(f),
}
}
Expand All @@ -32,10 +45,42 @@ impl ContextInput {
/// Returns the context input kind of a `ContextInput`.
pub fn kind(&self) -> u8 {
match self {
Self::Commitment(_) => CommitmentContextInput::KIND,
Self::BlockIssuanceCredit(_) => BlockIssuanceCreditContextInput::KIND,
Self::Reward(_) => RewardContextInput::KIND,
}
}

/// Checks whether the context input is a [`CommitmentContextInput`].
pub fn is_commitment(&self) -> bool {
matches!(self, Self::Commitment(_))
}

/// Gets the input as an actual [`CommitmentContextInput`].
/// PANIC: do not call on a non-commitment context input.
pub fn as_commitment(&self) -> &CommitmentContextInput {
if let Self::Commitment(input) = self {
input
} else {
panic!("invalid downcast of non-CommitmentContextInput");
}
}

/// Checks whether the context input is a [`BlockIssuanceCreditContextInput`].
pub fn is_block_issuance_credit(&self) -> bool {
matches!(self, Self::BlockIssuanceCredit(_))
}

/// Gets the input as an actual [`BlockIssuanceCreditContextInput`].
/// PANIC: do not call on a non-block-issuance-credit context input.
pub fn as_block_issuance_credit(&self) -> &BlockIssuanceCreditContextInput {
if let Self::BlockIssuanceCredit(input) = self {
input
} else {
panic!("invalid downcast of non-BlockIssuanceCreditContextInput");
}
}

/// Checks whether the context input is a [`RewardContextInput`].
pub fn is_reward(&self) -> bool {
matches!(self, Self::Reward(_))
Expand All @@ -44,40 +89,100 @@ impl ContextInput {
/// Gets the input as an actual [`RewardContextInput`].
/// PANIC: do not call on a non-reward context input.
pub fn as_reward(&self) -> &RewardContextInput {
let Self::Reward(input) = self;
input
if let Self::Reward(input) = self {
input
} else {
panic!("invalid downcast of non-RewardContextInput");
}
}
}

pub mod dto {
use serde::{Deserialize, Serialize};
#[cfg(test)]
mod tests {

use super::ContextInput;

pub use super::reward::dto::RewardContextInputDto;
use super::*;
use crate::types::block::Error;
#[test]
fn test_commitment() {
let commitment: ContextInput = serde_json::from_str(
r#"
{
"type": 0,
"commitmentId": "0xedf5f572c58ddf4b4f9567d82bf96689cc68b730df796d822b4b9fb643f5efda4f9567d82bf96689"
}
"#,
)
.unwrap();
assert!(commitment.is_commitment());
assert_eq!(
commitment.as_commitment().commitment_id().to_string(),
"0xedf5f572c58ddf4b4f9567d82bf96689cc68b730df796d822b4b9fb643f5efda4f9567d82bf96689"
);

/// Describes all the different context input types.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, From)]
#[serde(untagged)]
pub enum ContextInputDto {
Reward(RewardContextInputDto),
// Test wrong type returns error.
let commitment_deserialization_result: Result<ContextInput, _> = serde_json::from_str(
r#"
{
"type": 2,
"commitmentId": "0xedf5f572c58ddf4b4f9567d82bf96689cc68b730df796d822b4b9fb643f5efda4f9567d82bf96689"
}
"#,
);
assert!(commitment_deserialization_result.is_err());
}

impl From<&ContextInput> for ContextInputDto {
fn from(value: &ContextInput) -> Self {
match value {
ContextInput::Reward(u) => Self::Reward(u.into()),
#[test]
fn test_block_issuance_credit() {
let bic: ContextInput = serde_json::from_str(
r#"
{
"type": 1,
"accountId": "0x52fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649"
}
}
"#,
)
.unwrap();
assert!(bic.is_block_issuance_credit());
assert_eq!(
bic.as_block_issuance_credit().account_id().to_string(),
"0x52fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649"
);

// Test wrong type returns error.
let bic_deserialization_result: Result<ContextInput, _> = serde_json::from_str(
r#"
{
"type": 2,
"accountId": "0x52fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649"
}
"#,
);
assert!(bic_deserialization_result.is_err());
}

impl TryFrom<ContextInputDto> for ContextInput {
type Error = Error;
#[test]
fn test_reward() {
let reward: ContextInput = serde_json::from_str(
r#"
{
"type": 2,
"index": 10
}
"#,
)
.unwrap();
assert!(reward.is_reward());
assert_eq!(reward.as_reward().index(), 10);

fn try_from(value: ContextInputDto) -> Result<Self, Self::Error> {
match value {
ContextInputDto::Reward(u) => Ok(Self::Reward(u.try_into()?)),
// Test wrong type returns error.
let reward_serialization_result: Result<ContextInput, _> = serde_json::from_str(
r#"
{
"type": 0,
"index": 10
}
}
"#,
);
assert!(reward_serialization_result.is_err())
}
}
12 changes: 8 additions & 4 deletions sdk/src/types/block/context_input/reward.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,19 @@ impl RewardContextInput {
}
}

pub(crate) mod dto {
mod dto {
use alloc::format;

use serde::{Deserialize, Serialize};

use super::*;

/// A Reward Context Input is an input that indicates which transaction Input is claiming Mana rewards.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct RewardContextInputDto {
struct RewardContextInputDto {
#[serde(rename = "type")]
pub kind: u8,
pub index: u16,
kind: u8,
index: u16,
}

impl From<&RewardContextInput> for RewardContextInputDto {
Expand All @@ -49,4 +51,6 @@ pub(crate) mod dto {
Self::new(value.index)
}
}

impl_serde_typed_dto!(RewardContextInput, RewardContextInputDto, "reward context input");
}

0 comments on commit 0419ade

Please sign in to comment.