1pub mod milestone;
7pub mod tagged_data;
8pub mod transaction;
9pub mod treasury_transaction;
10
11use alloc::boxed::Box;
12use core::ops::Deref;
13
14use packable::{
15 error::{UnpackError, UnpackErrorExt},
16 packer::Packer,
17 unpacker::Unpacker,
18 Packable, PackableExt,
19};
20
21pub(crate) use self::{
22 milestone::{MilestoneMetadataLength, MilestoneOptionCount, ReceiptFundsCount, SignatureCount},
23 tagged_data::{TagLength, TaggedDataLength},
24 transaction::{InputCount, OutputCount},
25};
26pub use self::{
27 milestone::{MilestoneOptions, MilestonePayload},
28 tagged_data::TaggedDataPayload,
29 transaction::TransactionPayload,
30 treasury_transaction::TreasuryTransactionPayload,
31};
32use crate::{protocol::ProtocolParameters, Error};
33
34#[derive(Clone, Debug, Eq, PartialEq)]
36#[cfg_attr(
37 feature = "serde",
38 derive(serde::Serialize, serde::Deserialize),
39 serde(tag = "type", content = "data")
40)]
41pub enum Payload {
42 Transaction(Box<TransactionPayload>),
44 Milestone(Box<MilestonePayload>),
46 TreasuryTransaction(Box<TreasuryTransactionPayload>),
48 TaggedData(Box<TaggedDataPayload>),
50}
51
52impl From<TransactionPayload> for Payload {
53 fn from(payload: TransactionPayload) -> Self {
54 Self::Transaction(Box::new(payload))
55 }
56}
57
58impl From<MilestonePayload> for Payload {
59 fn from(payload: MilestonePayload) -> Self {
60 Self::Milestone(Box::new(payload))
61 }
62}
63
64impl From<TreasuryTransactionPayload> for Payload {
65 fn from(payload: TreasuryTransactionPayload) -> Self {
66 Self::TreasuryTransaction(Box::new(payload))
67 }
68}
69
70impl From<TaggedDataPayload> for Payload {
71 fn from(payload: TaggedDataPayload) -> Self {
72 Self::TaggedData(Box::new(payload))
73 }
74}
75
76impl Payload {
77 pub fn kind(&self) -> u32 {
79 match self {
80 Self::Transaction(_) => TransactionPayload::KIND,
81 Self::Milestone(_) => MilestonePayload::KIND,
82 Self::TreasuryTransaction(_) => TreasuryTransactionPayload::KIND,
83 Self::TaggedData(_) => TaggedDataPayload::KIND,
84 }
85 }
86}
87
88impl Packable for Payload {
89 type UnpackError = Error;
90 type UnpackVisitor = ProtocolParameters;
91
92 fn pack<P: Packer>(&self, packer: &mut P) -> Result<(), P::Error> {
93 match self {
94 Payload::Transaction(transaction) => {
95 TransactionPayload::KIND.pack(packer)?;
96 transaction.pack(packer)
97 }
98 Payload::Milestone(milestone) => {
99 MilestonePayload::KIND.pack(packer)?;
100 milestone.pack(packer)
101 }
102 Payload::TreasuryTransaction(treasury_transaction) => {
103 TreasuryTransactionPayload::KIND.pack(packer)?;
104 treasury_transaction.pack(packer)
105 }
106 Payload::TaggedData(tagged_data) => {
107 TaggedDataPayload::KIND.pack(packer)?;
108 tagged_data.pack(packer)
109 }
110 }?;
111
112 Ok(())
113 }
114
115 fn unpack<U: Unpacker, const VERIFY: bool>(
116 unpacker: &mut U,
117 visitor: &Self::UnpackVisitor,
118 ) -> Result<Self, UnpackError<Self::UnpackError, U::Error>> {
119 Ok(match u32::unpack::<_, VERIFY>(unpacker, &()).coerce()? {
120 TransactionPayload::KIND => {
121 Payload::from(TransactionPayload::unpack::<_, VERIFY>(unpacker, visitor).coerce()?)
122 }
123 MilestonePayload::KIND => Payload::from(MilestonePayload::unpack::<_, VERIFY>(unpacker, visitor).coerce()?),
124 TreasuryTransactionPayload::KIND => {
125 Payload::from(TreasuryTransactionPayload::unpack::<_, VERIFY>(unpacker, visitor).coerce()?)
126 }
127 TaggedDataPayload::KIND => Payload::from(TaggedDataPayload::unpack::<_, VERIFY>(unpacker, &()).coerce()?),
128 k => return Err(Error::InvalidPayloadKind(k)).map_err(UnpackError::Packable),
129 })
130 }
131}
132
133#[derive(Clone, Debug, Eq, PartialEq)]
136#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
137pub struct OptionalPayload(Option<Payload>);
138
139impl OptionalPayload {
140 fn pack_ref<P: Packer>(payload: &Payload, packer: &mut P) -> Result<(), P::Error> {
141 (payload.packed_len() as u32).pack(packer)?;
142 payload.pack(packer)
143 }
144}
145
146impl Deref for OptionalPayload {
147 type Target = Option<Payload>;
148
149 fn deref(&self) -> &Self::Target {
150 &self.0
151 }
152}
153
154impl Packable for OptionalPayload {
155 type UnpackError = Error;
156 type UnpackVisitor = ProtocolParameters;
157
158 fn pack<P: Packer>(&self, packer: &mut P) -> Result<(), P::Error> {
159 match &self.0 {
160 None => 0u32.pack(packer),
161 Some(payload) => Self::pack_ref(payload, packer),
162 }
163 }
164
165 fn unpack<U: Unpacker, const VERIFY: bool>(
166 unpacker: &mut U,
167 visitor: &Self::UnpackVisitor,
168 ) -> Result<Self, UnpackError<Self::UnpackError, U::Error>> {
169 let len = u32::unpack::<_, VERIFY>(unpacker, &()).coerce()? as usize;
170
171 if len > 0 {
172 unpacker.ensure_bytes(len)?;
173
174 let start_opt = unpacker.read_bytes();
175
176 let payload = Payload::unpack::<_, VERIFY>(unpacker, visitor)?;
177
178 let actual_len = if let (Some(start), Some(end)) = (start_opt, unpacker.read_bytes()) {
179 end - start
180 } else {
181 payload.packed_len()
182 };
183
184 if len != actual_len {
185 Err(UnpackError::Packable(Error::InvalidPayloadLength {
186 expected: len,
187 actual: actual_len,
188 }))
189 } else {
190 Ok(Self(Some(payload)))
191 }
192 } else {
193 Ok(Self(None))
194 }
195 }
196}
197
198impl From<Option<Payload>> for OptionalPayload {
200 fn from(option: Option<Payload>) -> Self {
201 Self(option)
202 }
203}
204
205#[allow(clippy::from_over_into)]
206impl Into<Option<Payload>> for OptionalPayload {
207 fn into(self) -> Option<Payload> {
208 self.0
209 }
210}
211
212#[cfg(feature = "dto")]
213#[allow(missing_docs)]
214pub mod dto {
215 use serde::{Deserialize, Serialize};
216
217 use super::*;
218 pub use super::{
219 milestone::dto::MilestonePayloadDto, tagged_data::dto::TaggedDataPayloadDto,
220 transaction::dto::TransactionPayloadDto, treasury_transaction::dto::TreasuryTransactionPayloadDto,
221 };
222 use crate::error::dto::DtoError;
223
224 #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
226 #[serde(untagged)]
227 pub enum PayloadDto {
228 Transaction(Box<TransactionPayloadDto>),
229 Milestone(Box<MilestonePayloadDto>),
230 TreasuryTransaction(Box<TreasuryTransactionPayloadDto>),
231 TaggedData(Box<TaggedDataPayloadDto>),
232 }
233
234 impl From<TransactionPayloadDto> for PayloadDto {
235 fn from(payload: TransactionPayloadDto) -> Self {
236 Self::Transaction(Box::new(payload))
237 }
238 }
239
240 impl From<MilestonePayloadDto> for PayloadDto {
241 fn from(payload: MilestonePayloadDto) -> Self {
242 Self::Milestone(Box::new(payload))
243 }
244 }
245
246 impl From<TreasuryTransactionPayloadDto> for PayloadDto {
247 fn from(payload: TreasuryTransactionPayloadDto) -> Self {
248 Self::TreasuryTransaction(Box::new(payload))
249 }
250 }
251
252 impl From<TaggedDataPayloadDto> for PayloadDto {
253 fn from(payload: TaggedDataPayloadDto) -> Self {
254 Self::TaggedData(Box::new(payload))
255 }
256 }
257
258 impl From<&Payload> for PayloadDto {
259 fn from(value: &Payload) -> Self {
260 match value {
261 Payload::Transaction(p) => PayloadDto::Transaction(Box::new(TransactionPayloadDto::from(p.as_ref()))),
262 Payload::Milestone(p) => PayloadDto::Milestone(Box::new(MilestonePayloadDto::from(p.as_ref()))),
263 Payload::TreasuryTransaction(p) => {
264 PayloadDto::TreasuryTransaction(Box::new(TreasuryTransactionPayloadDto::from(p.as_ref())))
265 }
266 Payload::TaggedData(p) => PayloadDto::TaggedData(Box::new(TaggedDataPayloadDto::from(p.as_ref()))),
267 }
268 }
269 }
270
271 impl Payload {
272 pub fn try_from_dto(value: &PayloadDto, protocol_parameters: &ProtocolParameters) -> Result<Payload, DtoError> {
273 Ok(match value {
274 PayloadDto::Transaction(p) => {
275 Payload::from(TransactionPayload::try_from_dto(p.as_ref(), protocol_parameters)?)
276 }
277 PayloadDto::Milestone(p) => {
278 Payload::from(MilestonePayload::try_from_dto(p.as_ref(), protocol_parameters)?)
279 }
280 PayloadDto::TreasuryTransaction(p) => Payload::from(TreasuryTransactionPayload::try_from_dto(
281 p.as_ref(),
282 protocol_parameters.token_supply(),
283 )?),
284 PayloadDto::TaggedData(p) => Payload::from(TaggedDataPayload::try_from(p.as_ref())?),
285 })
286 }
287 }
288}