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 CommitmentInput and BlockIssuanceCreditInput #981

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 Input (BIC Input) provides the VM with context for the value of
abdulmth marked this conversation as resolved.
Show resolved Hide resolved
/// the BIC vector of a specific slot.
DaughterOfMars marked this conversation as resolved.
Show resolved Hide resolved
#[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;
abdulmth marked this conversation as resolved.
Show resolved Hide resolved

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)
kwek20 marked this conversation as resolved.
Show resolved Hide resolved
}
}

impl_serde_typed_dto!(
BlockIssuanceCreditContextInput,
BlockIssuanceCreditContextInputDto,
"block issuance credit input"
Thoralf-M marked this conversation as resolved.
Show resolved Hide resolved
);
}
58 changes: 58 additions & 0 deletions sdk/src/types/block/context_input/commitment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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 input");
abdulmth marked this conversation as resolved.
Show resolved Hide resolved
}
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)]
abdulmth marked this conversation as resolved.
Show resolved Hide resolved
#[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 {
thibault-martinez marked this conversation as resolved.
Show resolved Hide resolved
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 input");
abdulmth marked this conversation as resolved.
Show resolved Hide resolved
}
Loading