bee_block/payload/milestone/
mod.rs1mod essence;
7mod index;
8mod merkle;
9mod milestone_id;
10
11pub mod option;
13
14use alloc::{string::String, vec::Vec};
15use core::{fmt::Debug, ops::RangeInclusive};
16
17use crypto::{signatures::ed25519, Error as CryptoError};
18use iterator_sorted::is_unique_sorted;
19pub(crate) use option::{MilestoneOptionCount, ReceiptFundsCount};
20use packable::{bounded::BoundedU8, prefix::VecPrefix, Packable};
21
22pub use self::{
23 essence::MilestoneEssence,
24 index::MilestoneIndex,
25 merkle::MerkleRoot,
26 milestone_id::MilestoneId,
27 option::{MilestoneOption, MilestoneOptions, ParametersMilestoneOption, ReceiptMilestoneOption},
28};
29pub(crate) use self::{essence::MilestoneMetadataLength, option::BinaryParametersLength};
30use crate::{protocol::ProtocolParameters, signature::Signature, Error};
31
32#[derive(Debug)]
33#[allow(missing_docs)]
34pub enum MilestoneValidationError {
35 InvalidMinThreshold,
36 TooFewSignatures(usize, usize),
37 InsufficientApplicablePublicKeys(usize, usize),
38 UnapplicablePublicKey(String),
39 InvalidSignature(usize, String),
40 Crypto(CryptoError),
41}
42
43impl From<CryptoError> for MilestoneValidationError {
44 fn from(error: CryptoError) -> Self {
45 MilestoneValidationError::Crypto(error)
46 }
47}
48
49pub(crate) type SignatureCount =
50 BoundedU8<{ *MilestonePayload::SIGNATURE_COUNT_RANGE.start() }, { *MilestonePayload::SIGNATURE_COUNT_RANGE.end() }>;
51
52#[derive(Clone, Debug, Eq, PartialEq, Packable)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
55#[packable(unpack_error = Error)]
56#[packable(unpack_visitor = ProtocolParameters)]
57pub struct MilestonePayload {
58 essence: MilestoneEssence,
59 #[packable(verify_with = verify_signatures_packable)]
60 #[packable(unpack_error_with = |e| e.unwrap_item_err_or_else(|p| Error::MilestoneInvalidSignatureCount(p.into())))]
61 signatures: VecPrefix<Signature, SignatureCount>,
62}
63
64impl MilestonePayload {
65 pub const KIND: u32 = 7;
67 pub const SIGNATURE_COUNT_RANGE: RangeInclusive<u8> = 1..=255;
69 pub const SIGNATURE_LENGTH: usize = 64;
71
72 pub fn new(essence: MilestoneEssence, signatures: Vec<Signature>) -> Result<Self, Error> {
74 let signatures = VecPrefix::<Signature, SignatureCount>::try_from(signatures)
75 .map_err(Error::MilestoneInvalidSignatureCount)?;
76
77 Ok(Self { essence, signatures })
78 }
79
80 pub fn essence(&self) -> &MilestoneEssence {
82 &self.essence
83 }
84
85 pub fn signatures(&self) -> &[Signature] {
87 &self.signatures
88 }
89
90 pub fn id(&self) -> MilestoneId {
92 MilestoneId::new(self.essence().hash())
93 }
94
95 pub fn validate(
97 &self,
98 applicable_public_keys: &[String],
99 min_threshold: usize,
100 ) -> Result<(), MilestoneValidationError> {
101 if min_threshold == 0 {
102 return Err(MilestoneValidationError::InvalidMinThreshold);
103 }
104
105 if applicable_public_keys.len() < min_threshold {
106 return Err(MilestoneValidationError::InsufficientApplicablePublicKeys(
107 applicable_public_keys.len(),
108 min_threshold,
109 ));
110 }
111
112 if self.signatures.len() < min_threshold {
113 return Err(MilestoneValidationError::TooFewSignatures(
114 min_threshold,
115 self.signatures.len(),
116 ));
117 }
118
119 let essence_hash = self.essence().hash();
120
121 for (index, signature) in self.signatures().iter().enumerate() {
122 let Signature::Ed25519(signature) = signature;
123
124 if !applicable_public_keys.contains(&hex::encode(signature.public_key())) {
125 return Err(MilestoneValidationError::UnapplicablePublicKey(prefix_hex::encode(
126 *signature.public_key(),
127 )));
128 }
129
130 let ed25519_public_key = ed25519::PublicKey::try_from_bytes(*signature.public_key())
131 .map_err(MilestoneValidationError::Crypto)?;
132 let ed25519_signature = ed25519::Signature::from_bytes(*signature.signature());
133
134 if !ed25519_public_key.verify(&ed25519_signature, &essence_hash) {
135 return Err(MilestoneValidationError::InvalidSignature(
136 index,
137 prefix_hex::encode(signature.public_key()),
138 ));
139 }
140 }
141
142 Ok(())
143 }
144}
145
146fn verify_signatures<const VERIFY: bool>(signatures: &[Signature]) -> Result<(), Error> {
147 if VERIFY
148 && !is_unique_sorted(signatures.iter().map(|signature| {
149 let Signature::Ed25519(signature) = signature;
150 signature.public_key()
151 }))
152 {
153 Err(Error::MilestoneSignaturesNotUniqueSorted)
154 } else {
155 Ok(())
156 }
157}
158
159fn verify_signatures_packable<const VERIFY: bool>(
160 signatures: &[Signature],
161 _visitor: &ProtocolParameters,
162) -> Result<(), Error> {
163 verify_signatures::<VERIFY>(signatures)
164}
165
166#[cfg(feature = "dto")]
167#[allow(missing_docs)]
168pub mod dto {
169 use core::str::FromStr;
170
171 use serde::{Deserialize, Serialize};
172
173 use self::option::dto::MilestoneOptionDto;
174 use super::*;
175 use crate::{
176 error::dto::DtoError, parent::Parents, payload::milestone::MilestoneIndex, signature::dto::SignatureDto,
177 BlockId,
178 };
179
180 #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
182 pub struct MilestonePayloadDto {
183 #[serde(rename = "type")]
184 pub kind: u32,
185 pub index: u32,
186 pub timestamp: u32,
187 #[serde(rename = "protocolVersion")]
188 pub protocol_version: u8,
189 #[serde(rename = "previousMilestoneId")]
190 pub previous_milestone_id: String,
191 pub parents: Vec<String>,
192 #[serde(rename = "inclusionMerkleRoot")]
193 pub inclusion_merkle_root: String,
194 #[serde(rename = "appliedMerkleRoot")]
195 pub applied_merkle_root: String,
196 #[serde(skip_serializing_if = "Vec::is_empty", default)]
197 pub options: Vec<MilestoneOptionDto>,
198 #[serde(skip_serializing_if = "String::is_empty", default)]
199 pub metadata: String,
200 pub signatures: Vec<SignatureDto>,
201 }
202
203 impl From<&MilestonePayload> for MilestonePayloadDto {
204 fn from(value: &MilestonePayload) -> Self {
205 MilestonePayloadDto {
206 kind: MilestonePayload::KIND,
207 index: *value.essence().index(),
208 timestamp: value.essence().timestamp(),
209 protocol_version: value.essence().protocol_version(),
210 previous_milestone_id: value.essence().previous_milestone_id().to_string(),
211 parents: value.essence().parents().iter().map(|p| p.to_string()).collect(),
212 inclusion_merkle_root: value.essence().inclusion_merkle_root().to_string(),
213 applied_merkle_root: value.essence().applied_merkle_root().to_string(),
214 metadata: prefix_hex::encode(value.essence().metadata()),
215 options: value.essence().options().iter().map(Into::into).collect::<_>(),
216 signatures: value.signatures().iter().map(From::from).collect(),
217 }
218 }
219 }
220
221 impl MilestonePayload {
222 pub fn try_from_dto(
223 value: &MilestonePayloadDto,
224 protocol_parameters: &ProtocolParameters,
225 ) -> Result<MilestonePayload, DtoError> {
226 let essence = {
227 let index = value.index;
228 let timestamp = value.timestamp;
229 let previous_milestone_id = MilestoneId::from_str(&value.previous_milestone_id)
230 .map_err(|_| DtoError::InvalidField("previousMilestoneId"))?;
231 let mut parent_ids = Vec::new();
232
233 for block_id in &value.parents {
234 parent_ids.push(
235 block_id
236 .parse::<BlockId>()
237 .map_err(|_| DtoError::InvalidField("parents"))?,
238 );
239 }
240
241 let inclusion_merkle_root = MerkleRoot::from_str(&value.inclusion_merkle_root)
242 .map_err(|_| DtoError::InvalidField("inclusionMerkleRoot"))?;
243 let applied_merkle_root = MerkleRoot::from_str(&value.applied_merkle_root)
244 .map_err(|_| DtoError::InvalidField("appliedMerkleRoot"))?;
245 let options = MilestoneOptions::try_from(
246 value
247 .options
248 .iter()
249 .map(|o| MilestoneOption::try_from_dto(o, protocol_parameters.token_supply()))
250 .collect::<Result<Vec<_>, _>>()?,
251 )?;
252 let metadata = if !value.metadata.is_empty() {
253 prefix_hex::decode(&value.metadata).map_err(|_| DtoError::InvalidField("metadata"))?
254 } else {
255 Vec::new()
256 };
257
258 MilestoneEssence::new(
259 MilestoneIndex(index),
260 timestamp,
261 protocol_parameters.protocol_version(),
262 previous_milestone_id,
263 Parents::new(parent_ids)?,
264 inclusion_merkle_root,
265 applied_merkle_root,
266 metadata,
267 options,
268 )?
269 };
270
271 let mut signatures = Vec::new();
272 for v in &value.signatures {
273 signatures.push(v.try_into().map_err(|_| DtoError::InvalidField("signatures"))?)
274 }
275
276 Ok(MilestonePayload::new(essence, signatures)?)
277 }
278 }
279}
280
281#[cfg(feature = "inx")]
282#[allow(missing_docs)]
283pub mod inx {
284 use packable::PackableExt;
285
286 use super::*;
287 use crate::payload::Payload;
288
289 impl From<MilestonePayload> for ::inx::proto::RawMilestone {
290 fn from(value: MilestonePayload) -> Self {
291 Self {
292 data: Payload::from(value).pack_to_vec(),
293 }
294 }
295 }
296}