bee_block/
block.rs

1// Copyright 2020-2021 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use core::ops::Deref;
5
6use bee_pow::providers::{miner::Miner, NonceProvider, NonceProviderBuilder};
7use crypto::hashes::{blake2b::Blake2b256, Digest};
8use packable::{
9    error::{UnexpectedEOF, UnpackError, UnpackErrorExt},
10    packer::Packer,
11    unpacker::{CounterUnpacker, SliceUnpacker, Unpacker},
12    Packable, PackableExt,
13};
14
15use crate::{
16    parent::Parents,
17    payload::{OptionalPayload, Payload},
18    protocol::ProtocolParameters,
19    BlockId, Error, PROTOCOL_VERSION,
20};
21
22/// A builder to build a [`Block`].
23#[derive(Clone)]
24#[must_use]
25pub struct BlockBuilder<P: NonceProvider = Miner> {
26    protocol_version: Option<u8>,
27    parents: Parents,
28    payload: Option<Payload>,
29    nonce_provider: Option<P>,
30}
31
32impl<P: NonceProvider> BlockBuilder<P> {
33    const DEFAULT_NONCE: u64 = 0;
34
35    /// Creates a new [`BlockBuilder`].
36    #[inline(always)]
37    pub fn new(parents: Parents) -> Self {
38        Self {
39            protocol_version: None,
40            parents,
41            payload: None,
42            nonce_provider: None,
43        }
44    }
45
46    /// Adds a protocol version to a [`BlockBuilder`].
47    #[inline(always)]
48    pub fn with_protocol_version(mut self, protocol_version: u8) -> Self {
49        self.protocol_version = Some(protocol_version);
50        self
51    }
52
53    /// Adds a payload to a [`BlockBuilder`].
54    #[inline(always)]
55    pub fn with_payload(mut self, payload: Payload) -> Self {
56        self.payload = Some(payload);
57        self
58    }
59
60    /// Adds a nonce provider to a [`BlockBuilder`].
61    #[inline(always)]
62    pub fn with_nonce_provider(mut self, nonce_provider: P) -> Self {
63        self.nonce_provider = Some(nonce_provider);
64        self
65    }
66
67    /// Finishes the [`BlockBuilder`] into a [`Block`].
68    pub fn finish(self, min_pow_score: u32) -> Result<Block, Error> {
69        verify_payload(self.payload.as_ref())?;
70
71        let mut block = Block {
72            protocol_version: self.protocol_version.unwrap_or(PROTOCOL_VERSION),
73            parents: self.parents,
74            payload: self.payload.into(),
75            nonce: 0,
76        };
77
78        let block_bytes = block.pack_to_vec();
79
80        if block_bytes.len() > Block::LENGTH_MAX {
81            return Err(Error::InvalidBlockLength(block_bytes.len()));
82        }
83
84        let nonce_provider = self.nonce_provider.unwrap_or_else(|| P::Builder::new().finish());
85
86        block.nonce = nonce_provider
87            .nonce(
88                &block_bytes[..block_bytes.len() - core::mem::size_of::<u64>()],
89                min_pow_score,
90            )
91            .unwrap_or(Self::DEFAULT_NONCE);
92
93        Ok(block)
94    }
95}
96
97/// Represent the object that nodes gossip around the network.
98#[derive(Clone, Debug, Eq, PartialEq)]
99#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
100pub struct Block {
101    /// Protocol version of the block.
102    protocol_version: u8,
103    /// The [`BlockId`]s that this block directly approves.
104    parents: Parents,
105    /// The optional [Payload] of the block.
106    payload: OptionalPayload,
107    /// The result of the Proof of Work in order for the block to be accepted into the tangle.
108    nonce: u64,
109}
110
111impl Block {
112    /// The minimum number of bytes in a block.
113    pub const LENGTH_MIN: usize = 46;
114    /// The maximum number of bytes in a block.
115    pub const LENGTH_MAX: usize = 32768;
116
117    /// Creates a new [`BlockBuilder`] to construct an instance of a [`Block`].
118    #[inline(always)]
119    pub fn build(parents: Parents) -> BlockBuilder {
120        BlockBuilder::new(parents)
121    }
122
123    /// Returns the protocol version of a [`Block`].
124    #[inline(always)]
125    pub fn protocol_version(&self) -> u8 {
126        self.protocol_version
127    }
128
129    /// Returns the parents of a [`Block`].
130    #[inline(always)]
131    pub fn parents(&self) -> &Parents {
132        &self.parents
133    }
134
135    /// Returns the optional payload of a [`Block`].
136    #[inline(always)]
137    pub fn payload(&self) -> Option<&Payload> {
138        self.payload.as_ref()
139    }
140
141    /// Returns the nonce of a [`Block`].
142    #[inline(always)]
143    pub fn nonce(&self) -> u64 {
144        self.nonce
145    }
146
147    /// Computes the identifier of the block.
148    #[inline(always)]
149    pub fn id(&self) -> BlockId {
150        BlockId::new(Blake2b256::digest(&self.pack_to_vec()).into())
151    }
152
153    /// Consumes the [`Block`], and returns ownership over its [`Parents`].
154    #[inline(always)]
155    pub fn into_parents(self) -> Parents {
156        self.parents
157    }
158
159    /// Unpacks a [`Block`] from a sequence of bytes doing syntactical checks and verifying that
160    /// there are no trailing bytes in the sequence.
161    pub fn unpack_strict<T: AsRef<[u8]>>(
162        bytes: T,
163        visitor: &<Self as Packable>::UnpackVisitor,
164    ) -> Result<Self, UnpackError<<Self as Packable>::UnpackError, UnexpectedEOF>> {
165        let mut unpacker = CounterUnpacker::new(SliceUnpacker::new(bytes.as_ref()));
166        let block = Self::unpack::<_, true>(&mut unpacker, visitor)?;
167
168        // When parsing the block is complete, there should not be any trailing bytes left that were not parsed.
169        if u8::unpack::<_, true>(&mut unpacker, &()).is_ok() {
170            return Err(UnpackError::Packable(Error::RemainingBytesAfterBlock));
171        }
172
173        Ok(block)
174    }
175}
176
177impl Packable for Block {
178    type UnpackError = Error;
179    type UnpackVisitor = ProtocolParameters;
180
181    fn pack<P: Packer>(&self, packer: &mut P) -> Result<(), P::Error> {
182        self.protocol_version.pack(packer)?;
183        self.parents.pack(packer)?;
184        self.payload.pack(packer)?;
185        self.nonce.pack(packer)?;
186
187        Ok(())
188    }
189
190    fn unpack<U: Unpacker, const VERIFY: bool>(
191        unpacker: &mut U,
192        visitor: &Self::UnpackVisitor,
193    ) -> Result<Self, UnpackError<Self::UnpackError, U::Error>> {
194        let start_opt = unpacker.read_bytes();
195
196        let protocol_version = u8::unpack::<_, VERIFY>(unpacker, &()).coerce()?;
197
198        if VERIFY && protocol_version != visitor.protocol_version() {
199            return Err(UnpackError::Packable(Error::ProtocolVersionMismatch {
200                expected: visitor.protocol_version(),
201                actual: protocol_version,
202            }));
203        }
204
205        let parents = Parents::unpack::<_, VERIFY>(unpacker, &())?;
206        let payload = OptionalPayload::unpack::<_, VERIFY>(unpacker, visitor)?;
207
208        if VERIFY {
209            verify_payload(payload.deref().as_ref()).map_err(UnpackError::Packable)?;
210        }
211
212        let nonce = u64::unpack::<_, VERIFY>(unpacker, &()).coerce()?;
213
214        let block = Self {
215            protocol_version,
216            parents,
217            payload,
218            nonce,
219        };
220
221        if VERIFY {
222            let block_len = if let (Some(start), Some(end)) = (start_opt, unpacker.read_bytes()) {
223                end - start
224            } else {
225                block.packed_len()
226            };
227
228            if block_len > Block::LENGTH_MAX {
229                return Err(UnpackError::Packable(Error::InvalidBlockLength(block_len)));
230            }
231        }
232
233        Ok(block)
234    }
235}
236
237fn verify_payload(payload: Option<&Payload>) -> Result<(), Error> {
238    if !matches!(
239        payload,
240        None | Some(Payload::Transaction(_)) | Some(Payload::Milestone(_)) | Some(Payload::TaggedData(_))
241    ) {
242        // Safe to unwrap since it's known not to be None.
243        Err(Error::InvalidPayloadKind(payload.unwrap().kind()))
244    } else {
245        Ok(())
246    }
247}
248
249#[cfg(feature = "dto")]
250#[allow(missing_docs)]
251pub mod dto {
252    use serde::{Deserialize, Serialize};
253
254    use super::*;
255    use crate::{error::dto::DtoError, payload::dto::PayloadDto, protocol::ProtocolParameters};
256
257    /// The block object that nodes gossip around in the network.
258    #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
259    pub struct BlockDto {
260        ///
261        #[serde(rename = "protocolVersion")]
262        pub protocol_version: u8,
263        ///
264        pub parents: Vec<String>,
265        ///
266        #[serde(skip_serializing_if = "Option::is_none")]
267        pub payload: Option<PayloadDto>,
268        ///
269        pub nonce: String,
270    }
271
272    impl From<&Block> for BlockDto {
273        fn from(value: &Block) -> Self {
274            BlockDto {
275                protocol_version: value.protocol_version(),
276                parents: value.parents().iter().map(BlockId::to_string).collect(),
277                payload: value.payload().map(Into::into),
278                nonce: value.nonce().to_string(),
279            }
280        }
281    }
282
283    impl Block {
284        pub fn try_from_dto(value: &BlockDto, protocol_parameters: &ProtocolParameters) -> Result<Block, DtoError> {
285            let parents = Parents::new(
286                value
287                    .parents
288                    .iter()
289                    .map(|m| m.parse::<BlockId>().map_err(|_| DtoError::InvalidField("parents")))
290                    .collect::<Result<Vec<BlockId>, DtoError>>()?,
291            )?;
292
293            let mut builder = BlockBuilder::new(parents)
294                .with_protocol_version(value.protocol_version)
295                .with_nonce_provider(
296                    value
297                        .nonce
298                        .parse::<u64>()
299                        .map_err(|_| DtoError::InvalidField("nonce"))?,
300                );
301            if let Some(p) = value.payload.as_ref() {
302                builder = builder.with_payload(Payload::try_from_dto(p, protocol_parameters)?);
303            }
304
305            Ok(builder.finish(protocol_parameters.min_pow_score())?)
306        }
307    }
308}