bee_block/payload/transaction/
mod.rs

1// Copyright 2020-2021 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4//! Module describing the transaction payload.
5
6mod essence;
7mod transaction_id;
8
9use crypto::hashes::{blake2b::Blake2b256, Digest};
10use packable::{error::UnpackError, packer::Packer, unpacker::Unpacker, Packable, PackableExt};
11
12pub(crate) use self::essence::{InputCount, OutputCount};
13pub use self::{
14    essence::{RegularTransactionEssence, RegularTransactionEssenceBuilder, TransactionEssence},
15    transaction_id::TransactionId,
16};
17use crate::{protocol::ProtocolParameters, unlock::Unlocks, Error};
18
19/// A transaction to move funds.
20#[derive(Clone, Debug, Eq, PartialEq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub struct TransactionPayload {
23    essence: TransactionEssence,
24    unlocks: Unlocks,
25}
26
27impl TransactionPayload {
28    /// The payload kind of a [`TransactionPayload`].
29    pub const KIND: u32 = 6;
30
31    /// Creates a new [`TransactionPayload`].
32    pub fn new(essence: TransactionEssence, unlocks: Unlocks) -> Result<TransactionPayload, Error> {
33        verify_essence_unlocks(&essence, &unlocks)?;
34
35        Ok(TransactionPayload { essence, unlocks })
36    }
37
38    /// Return the essence of a [`TransactionPayload`].
39    pub fn essence(&self) -> &TransactionEssence {
40        &self.essence
41    }
42
43    /// Return unlocks of a [`TransactionPayload`].
44    pub fn unlocks(&self) -> &Unlocks {
45        &self.unlocks
46    }
47
48    /// Computes the identifier of a [`TransactionPayload`].
49    pub fn id(&self) -> TransactionId {
50        let mut hasher = Blake2b256::new();
51
52        hasher.update(Self::KIND.to_le_bytes());
53        hasher.update(self.pack_to_vec());
54
55        TransactionId::new(hasher.finalize().into())
56    }
57}
58
59impl Packable for TransactionPayload {
60    type UnpackError = Error;
61    type UnpackVisitor = ProtocolParameters;
62
63    fn pack<P: Packer>(&self, packer: &mut P) -> Result<(), P::Error> {
64        self.essence.pack(packer)?;
65        self.unlocks.pack(packer)?;
66
67        Ok(())
68    }
69
70    fn unpack<U: Unpacker, const VERIFY: bool>(
71        unpacker: &mut U,
72        visitor: &Self::UnpackVisitor,
73    ) -> Result<Self, UnpackError<Self::UnpackError, U::Error>> {
74        let essence = TransactionEssence::unpack::<_, VERIFY>(unpacker, visitor)?;
75        let unlocks = Unlocks::unpack::<_, VERIFY>(unpacker, &())?;
76
77        if VERIFY {
78            verify_essence_unlocks(&essence, &unlocks).map_err(UnpackError::Packable)?;
79        }
80
81        Ok(TransactionPayload { essence, unlocks })
82    }
83}
84
85fn verify_essence_unlocks(essence: &TransactionEssence, unlocks: &Unlocks) -> Result<(), Error> {
86    match essence {
87        TransactionEssence::Regular(ref essence) => {
88            if essence.inputs().len() != unlocks.len() {
89                return Err(Error::InputUnlockCountMismatch {
90                    input_count: essence.inputs().len(),
91                    unlock_count: unlocks.len(),
92                });
93            }
94        }
95    }
96
97    Ok(())
98}
99
100#[cfg(feature = "dto")]
101#[allow(missing_docs)]
102pub mod dto {
103    use serde::{Deserialize, Serialize};
104
105    pub use super::essence::dto::{RegularTransactionEssenceDto, TransactionEssenceDto};
106    use super::*;
107    use crate::{error::dto::DtoError, unlock::dto::UnlockDto};
108
109    /// The payload type to define a value transaction.
110    #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
111    pub struct TransactionPayloadDto {
112        #[serde(rename = "type")]
113        pub kind: u32,
114        pub essence: TransactionEssenceDto,
115        pub unlocks: Vec<UnlockDto>,
116    }
117
118    impl From<&TransactionPayload> for TransactionPayloadDto {
119        fn from(value: &TransactionPayload) -> Self {
120            TransactionPayloadDto {
121                kind: TransactionPayload::KIND,
122                essence: value.essence().into(),
123                unlocks: value.unlocks().iter().map(Into::into).collect::<Vec<_>>(),
124            }
125        }
126    }
127
128    impl TransactionPayload {
129        pub fn try_from_dto(
130            value: &TransactionPayloadDto,
131            protocol_parameters: &ProtocolParameters,
132        ) -> Result<TransactionPayload, DtoError> {
133            let mut unlocks = Vec::new();
134
135            for b in &value.unlocks {
136                unlocks.push(b.try_into()?);
137            }
138
139            Ok(TransactionPayload::new(
140                TransactionEssence::try_from_dto(&value.essence, protocol_parameters)?,
141                Unlocks::new(unlocks)?,
142            )?)
143        }
144    }
145}