exocore_chain/
block.rs

1use std::borrow::Borrow;
2
3use bytes::{Bytes, BytesMut};
4use exocore_core::{
5    cell::{Cell, CellNodeRole, FullCell, NodeId},
6    framing::{
7        CapnpFrameBuilder, FrameBuilder, FrameReader, MultihashFrame, MultihashFrameBuilder,
8        PaddedFrame, PaddedFrameBuilder, SizedFrame, SizedFrameBuilder, TypedCapnpFrame,
9    },
10    sec::{
11        hash::{Multihash, MultihashDigestExt, Sha3_256},
12        signature::Signature,
13    },
14};
15use exocore_protos::{
16    capnp,
17    generated::data_chain_capnp::{
18        block_header, block_operation_header, block_signature, block_signatures,
19    },
20};
21
22use crate::{data::Data, operation::OperationId};
23
24pub type BlockOffset = u64;
25pub type BlockHeight = u64;
26pub type BlockOperationsSize = u32;
27pub type BlockSignaturesSize = u16;
28
29pub type BlockHeaderFrame<I> =
30    TypedCapnpFrame<MultihashFrame<32, Sha3_256, SizedFrame<I>>, block_header::Owned>;
31pub type BlockHeaderFrameBuilder =
32    SizedFrameBuilder<MultihashFrameBuilder<32, Sha3_256, CapnpFrameBuilder<block_header::Owned>>>;
33pub type SignaturesFrame<I> = TypedCapnpFrame<PaddedFrame<SizedFrame<I>>, block_signatures::Owned>;
34
35/// A trait representing a block stored or to be stored in the chain.
36/// It can either be a referenced block (`BlockRef`) or a in-memory block
37/// (`BlockOwned`).
38///
39/// A block consists of 3 parts:
40///  * Block header
41///  * Operations' bytes (capnp serialized `chain_operation` frames)
42///  * Block signatures
43///
44/// The block header and operations' data are the same on all nodes. Since a
45/// node writes a block as soon as it has enough signatures, signatures can
46/// differ from one node to the other. Signatures frame is pre-allocated, which
47/// means that not all signatures may fit. But in theory, it should always
48/// contain enough space for all nodes to add their own signature.
49pub trait Block {
50    type UnderlyingFrame: FrameReader<OwnedType = Bytes>;
51
52    fn offset(&self) -> BlockOffset;
53    fn header(&self) -> &BlockHeaderFrame<Self::UnderlyingFrame>;
54    fn operations_data(&self) -> &[u8];
55    fn signatures(&self) -> &SignaturesFrame<Self::UnderlyingFrame>;
56
57    #[inline]
58    fn total_size(&self) -> usize {
59        self.header().whole_data_size()
60            + self.operations_data().len()
61            + self.signatures().whole_data_size()
62    }
63
64    #[inline]
65    fn next_offset(&self) -> BlockOffset {
66        self.offset() + self.total_size() as BlockOffset
67    }
68
69    #[inline]
70    fn copy_data_into(&self, data: &mut [u8]) {
71        let operations_data = self.operations_data();
72        let operations_offset = self.header().whole_data_size();
73        let signatures_offset = operations_offset + operations_data.len();
74
75        self.header()
76            .copy_into(data)
77            .expect("Couldn't write block into given buffer");
78
79        data[operations_offset..signatures_offset].copy_from_slice(operations_data);
80
81        self.signatures()
82            .copy_into(&mut data[signatures_offset..])
83            .expect("Couldn't write signatures into given buffer");
84    }
85
86    fn as_data_vec(&self) -> Bytes {
87        Bytes::from(
88            [
89                self.header().whole_data(),
90                self.operations_data(),
91                self.signatures().whole_data(),
92            ]
93            .concat(),
94        )
95    }
96
97    fn to_owned(&self) -> DataBlock<Bytes> {
98        DataBlock {
99            offset: self.offset(),
100            header: self.header().to_owned(),
101            operations_data: Bytes::from(self.operations_data().to_vec()),
102            signatures: self.signatures().to_owned(),
103        }
104    }
105
106    fn get_height(&self) -> Result<BlockHeight, Error> {
107        let reader = self.header().get_reader()?;
108        Ok(reader.get_height())
109    }
110
111    fn get_proposed_operation_id(&self) -> Result<OperationId, Error> {
112        let reader = self.header().get_reader()?;
113        Ok(reader.get_proposed_operation_id())
114    }
115
116    fn operations_iter(&self) -> Result<BlockOperationsIterator, Error> {
117        let block_header = self.header().get_reader()?;
118        let operations_header = block_header
119            .get_operations_header()?
120            .iter()
121            .map(|reader| BlockOperationHeader::from_reader(&reader))
122            .collect::<Vec<_>>();
123
124        Ok(BlockOperationsIterator {
125            index: 0,
126            operations_header,
127            operations_data: self.operations_data(),
128            last_error: None,
129        })
130    }
131
132    fn get_operation(
133        &self,
134        operation_id: OperationId,
135    ) -> Result<Option<crate::operation::OperationFrame<&[u8]>>, Error> {
136        let block_header = self.header().get_reader()?;
137        let operations_header: Vec<BlockOperationHeader> = block_header
138            .get_operations_header()?
139            .iter()
140            .map(|reader| BlockOperationHeader::from_reader(&reader))
141            .collect();
142
143        let operation_index =
144            operations_header.binary_search_by_key(&operation_id, |header| header.operation_id);
145
146        if let Ok(operation_index) = operation_index {
147            if operation_index > operations_header.len() {
148                return Err(Error::OutOfBound(format!(
149                    "Operation id={} of block={} had an invalid index {} out of {} operations",
150                    operation_id,
151                    self.offset(),
152                    operation_index,
153                    operations_header.len()
154                )));
155            }
156
157            let frame = operations_header[operation_index].read_frame(self.operations_data())?;
158
159            Ok(Some(frame))
160        } else {
161            Ok(None)
162        }
163    }
164
165    fn validate<PB: Block>(&self, previous_block: Option<PB>) -> Result<(), Error> {
166        // TODO: Signature ticket: https://github.com/appaquet/exocore/issues/46
167        //       Should actually check signatures too
168
169        let header = self.header();
170        let header_reader: block_header::Reader = header.get_reader()?;
171
172        header.inner().inner().verify()?;
173
174        if let Some(previous_block) = previous_block {
175            let previous_hash = previous_block.header().inner().inner().multihash_bytes();
176            if previous_hash != header_reader.get_previous_hash()? {
177                return Err(Error::Integrity(
178                    "Hash of previous block doesn't match current block hash".to_string(),
179                ));
180            }
181        }
182
183        let sig_size_header = header_reader.get_signatures_size() as usize;
184        let sig_size_stored = self.signatures().whole_data_size();
185        if sig_size_header != sig_size_stored {
186            return Err(Error::Integrity(format!(
187                "Signatures size don't match: sig_size_header={}, sig_size_stored={}",
188                sig_size_header, sig_size_stored
189            )));
190        }
191
192        let ops_size_header = header_reader.get_operations_size() as usize;
193        let ops_size_stored = self.operations_data().len();
194        if ops_size_header != ops_size_stored {
195            return Err(Error::Integrity(format!(
196                "Operations size don't match: ops_size_header={}, ops_size_stored={}",
197                ops_size_header, ops_size_stored
198            )));
199        }
200
201        if ops_size_header > 0 {
202            let operations = self.operations_iter()?;
203            let ops_hash_stored = BlockOperations::hash_operations(operations)?;
204            let ops_hash_header = Multihash::<32>::from_bytes(header_reader.get_operations_hash()?)
205                .map_err(|err| {
206                    Error::Integrity(format!("Hash in block header couldn't be decoded: {}", err))
207                })?;
208
209            if ops_hash_stored != ops_hash_header {
210                return Err(Error::Integrity(format!(
211                    "Operations hash don't match: ops_hash_header={:?}, ops_hash_stored={:?}",
212                    ops_hash_header, ops_hash_stored
213                )));
214            }
215        }
216
217        Ok(())
218    }
219}
220
221/// Reads block header frame from an underlying frame (or just data)
222pub fn read_header_frame<I: FrameReader>(inner: I) -> Result<BlockHeaderFrame<I>, Error> {
223    let sized_frame = SizedFrame::new(inner)?;
224    let multihash_frame = MultihashFrame::new(sized_frame)?;
225    let frame = TypedCapnpFrame::new(multihash_frame)?;
226    Ok(frame)
227}
228
229pub fn read_header_frame_from_next_offset<I: FrameReader>(
230    inner: I,
231    next_offset: usize,
232) -> Result<BlockHeaderFrame<I>, Error> {
233    let sized_frame = SizedFrame::new_from_next_offset(inner, next_offset)?;
234    let multihash_frame = MultihashFrame::new(sized_frame)?;
235    let frame = TypedCapnpFrame::new(multihash_frame)?;
236    Ok(frame)
237}
238
239pub fn build_header_frame(
240    header: CapnpFrameBuilder<block_header::Owned>,
241) -> BlockHeaderFrameBuilder {
242    SizedFrameBuilder::new(MultihashFrameBuilder::<32, Sha3_256, _>::new(header))
243}
244
245/// Block from an arbitrary type of data.
246pub struct DataBlock<D: Data> {
247    pub offset: BlockOffset,
248    pub header: BlockHeaderFrame<D>,
249    pub operations_data: D,
250    pub signatures: SignaturesFrame<D>,
251}
252
253impl<D: Data> DataBlock<D> {
254    pub fn new(data: D) -> Result<DataBlock<D>, Error> {
255        let header = read_header_frame(data.clone())?;
256        let header_reader: block_header::Reader = header.get_reader()?;
257
258        let operations_offset = header.whole_data_size();
259        let operations_size = header_reader.get_operations_size() as usize;
260        let signatures_offset = operations_offset + operations_size;
261        let signatures_size = header_reader.get_signatures_size() as usize;
262
263        if signatures_offset >= data.len() {
264            return Err(Error::OutOfBound(format!(
265                "Signature offset {} is after data len {}",
266                signatures_offset,
267                data.len()
268            )));
269        }
270
271        let signatures_data = data.view(signatures_offset..signatures_offset + signatures_size);
272        let signatures = BlockSignatures::read_frame(signatures_data)?;
273
274        let operations_data = data.view(operations_offset..signatures_offset);
275
276        Ok(DataBlock {
277            offset: header_reader.get_offset(),
278            header,
279            operations_data,
280            signatures,
281        })
282    }
283
284    pub fn new_from_next_offset(data: D, next_offset: usize) -> Result<DataBlock<D>, Error> {
285        let signatures = BlockSignatures::read_frame_from_next_offset(data.clone(), next_offset)?;
286        let signatures_reader: block_signatures::Reader = signatures.get_reader()?;
287        let signatures_offset = next_offset - signatures.whole_data_size();
288
289        let operations_size = signatures_reader.get_operations_size() as usize;
290        if operations_size > signatures_offset {
291            return Err(Error::OutOfBound(format!(
292                "Tried to read block from next offset {}, but its operations size would exceed beginning of file (operations_size={} signatures_offset={})",
293                next_offset, operations_size, signatures_offset,
294            )));
295        }
296
297        let operations_offset = signatures_offset - operations_size;
298        let operations_data = data.view(operations_offset..signatures_offset);
299
300        let header = read_header_frame_from_next_offset(data, operations_offset)?;
301        let header_reader: block_header::Reader = header.get_reader()?;
302
303        Ok(DataBlock {
304            offset: header_reader.get_offset(),
305            operations_data,
306            header,
307            signatures,
308        })
309    }
310}
311
312impl<D: Data> Block for DataBlock<D> {
313    type UnderlyingFrame = D;
314
315    fn offset(&self) -> u64 {
316        self.offset
317    }
318
319    fn header(&self) -> &BlockHeaderFrame<Self::UnderlyingFrame> {
320        &self.header
321    }
322
323    fn operations_data(&self) -> &[u8] {
324        self.operations_data.slice(..)
325    }
326
327    fn signatures(&self) -> &SignaturesFrame<Self::UnderlyingFrame> {
328        &self.signatures
329    }
330}
331
332/// In-memory block.
333pub struct BlockBuilder;
334
335impl BlockBuilder {
336    pub fn build(
337        offset: BlockOffset,
338        header: BlockHeaderFrame<Bytes>,
339        operations_data: Bytes,
340        signatures: SignaturesFrame<Bytes>,
341    ) -> DataBlock<Bytes> {
342        DataBlock {
343            offset,
344            header,
345            operations_data,
346            signatures,
347        }
348    }
349
350    pub fn build_genesis(full_cell: &FullCell) -> Result<DataBlock<Bytes>, Error> {
351        let operations = BlockOperations::empty();
352        let block = Self::build_with_prev_info(full_cell.cell(), 0, 0, 0, &[], 0, operations)?;
353        // TODO: Add master signature after doing https://github.com/appaquet/exocore/issues/46
354        Ok(block)
355    }
356
357    pub fn build_with_prev_block<B>(
358        cell: &Cell,
359        previous_block: &B,
360        proposed_operation_id: u64,
361        operations: BlockOperations,
362    ) -> Result<DataBlock<Bytes>, Error>
363    where
364        B: Block,
365    {
366        let previous_block_header_reader = previous_block.header().get_reader()?;
367
368        let previous_offset = previous_block.offset();
369        let previous_hash = previous_block.header().inner().inner().multihash_bytes();
370
371        let offset = previous_block.next_offset();
372        let height = previous_block_header_reader.get_height();
373
374        Self::build_with_prev_info(
375            cell,
376            offset,
377            height,
378            previous_offset,
379            previous_hash,
380            proposed_operation_id,
381            operations,
382        )
383    }
384
385    pub fn build_with_prev_info(
386        cell: &Cell,
387        offset: BlockOffset,
388        height: BlockHeight,
389        previous_offset: BlockOffset,
390        previous_hash: &[u8],
391        proposed_operation_id: u64,
392        operations: BlockOperations,
393    ) -> Result<DataBlock<Bytes>, Error> {
394        let local_node = cell.local_node();
395        let operations_data_size = operations.data.len() as u32;
396
397        // initialize block header
398        let mut header_frame_builder = CapnpFrameBuilder::<block_header::Owned>::new();
399        let mut header_msg_builder = header_frame_builder.get_builder();
400        header_msg_builder.set_offset(offset);
401        header_msg_builder.set_height(height + 1);
402        header_msg_builder.set_previous_offset(previous_offset);
403        header_msg_builder.set_previous_hash(previous_hash);
404        header_msg_builder.set_proposed_operation_id(proposed_operation_id);
405        header_msg_builder.set_proposed_node_id(local_node.id().to_string().as_str());
406        header_msg_builder.set_operations_size(operations_data_size);
407        header_msg_builder.set_operations_hash(&operations.hash.to_bytes());
408
409        let mut operations_builder = header_msg_builder
410            .reborrow()
411            .init_operations_header(operations.headers.len() as u32);
412        for (i, header_builder) in operations.headers.iter().enumerate() {
413            let mut entry_builder = operations_builder.reborrow().get(i as u32);
414            header_builder.copy_into_builder(&mut entry_builder);
415        }
416
417        // create an empty signature for each node as a placeholder to find the size
418        // required for signatures
419        let signature_frame = BlockSignatures::empty_signatures_for_nodes(cell)
420            .to_frame_for_new_block(operations_data_size)?;
421
422        // set required signatures size in block
423        header_msg_builder
424            .set_signatures_size(signature_frame.whole_data_size() as BlockSignaturesSize);
425
426        // serialize block header and then re-read it
427        let final_frame_builder = build_header_frame(header_frame_builder);
428        let final_frame_data = final_frame_builder.as_bytes();
429        let block_header = read_header_frame(final_frame_data)?;
430
431        Ok(DataBlock {
432            offset,
433            header: block_header,
434            operations_data: operations.data,
435            signatures: signature_frame,
436        })
437    }
438}
439
440/// Iterator over operations stored in a block.
441pub struct BlockOperationsIterator<'a> {
442    index: usize,
443    operations_header: Vec<BlockOperationHeader>,
444    operations_data: &'a [u8],
445    last_error: Option<Error>,
446}
447
448impl<'a> Iterator for BlockOperationsIterator<'a> {
449    type Item = crate::operation::OperationFrame<&'a [u8]>;
450
451    fn next(&mut self) -> Option<Self::Item> {
452        if self.index >= self.operations_header.len() {
453            return None;
454        }
455
456        let header = &self.operations_header[self.index];
457        self.index += 1;
458
459        let frame_res = header.read_frame(self.operations_data);
460        match frame_res {
461            Ok(frame) => Some(frame),
462            Err(err) => {
463                self.last_error = Some(err);
464                None
465            }
466        }
467    }
468
469    fn size_hint(&self) -> (usize, Option<usize>) {
470        (self.index, Some(self.operations_data.len()))
471    }
472}
473
474/// Wraps operations header stored in a block.
475pub struct BlockOperations {
476    hash: Multihash<32>,
477    headers: Vec<BlockOperationHeader>,
478    data: Bytes,
479}
480
481impl BlockOperations {
482    pub fn empty() -> BlockOperations {
483        BlockOperations {
484            hash: Multihash::default(),
485            headers: Vec::new(),
486            data: Bytes::new(),
487        }
488    }
489
490    pub fn from_operations<I, M, F>(sorted_operations: I) -> Result<BlockOperations, Error>
491    where
492        I: Iterator<Item = M>,
493        M: Borrow<crate::operation::OperationFrame<F>>,
494        F: FrameReader,
495    {
496        let mut hasher = Sha3_256::default();
497        let mut headers = Vec::new();
498        let mut data = BytesMut::new();
499        let mut last_operation_id = 0;
500
501        for operation in sorted_operations {
502            let operation = operation.borrow();
503            let operation_reader = operation.get_reader()?;
504            let offset = data.len();
505            let entry_data = operation.whole_data();
506            hasher.input_signed_frame(operation.inner().inner());
507            data.extend_from_slice(entry_data);
508
509            let operation_id = operation_reader.get_operation_id();
510            if operation_id < last_operation_id {
511                panic!(
512                    "Tried to build a block from unsorted operations op={} < last={}",
513                    operation_id, last_operation_id
514                );
515            }
516            last_operation_id = operation_id;
517
518            headers.push(BlockOperationHeader {
519                operation_id,
520                data_offset: offset as u32,
521                data_size: (data.len() - offset) as u32,
522            });
523        }
524
525        Ok(BlockOperations {
526            hash: hasher.to_multihash(),
527            headers,
528            data: data.into(),
529        })
530    }
531
532    pub fn hash_operations<I, M, F>(sorted_operations: I) -> Result<Multihash<32>, Error>
533    where
534        I: Iterator<Item = M>,
535        M: Borrow<crate::operation::OperationFrame<F>>,
536        F: FrameReader,
537    {
538        let mut hasher = Sha3_256::default();
539        for operation in sorted_operations {
540            hasher.input_signed_frame(operation.borrow().inner().inner());
541        }
542        Ok(hasher.to_multihash())
543    }
544
545    pub fn operations_count(&self) -> usize {
546        self.headers.len()
547    }
548
549    pub fn operations_id(&self) -> impl Iterator<Item = OperationId> + '_ {
550        self.headers.iter().map(|header| header.operation_id)
551    }
552
553    pub fn multihash(&self) -> Multihash<32> {
554        self.hash
555    }
556
557    pub fn data(&self) -> &[u8] {
558        &self.data
559    }
560}
561
562/// Header of an operation stored within a block. It represents the position in
563/// the bytes of the block.
564struct BlockOperationHeader {
565    operation_id: u64,
566    data_offset: u32,
567    data_size: u32,
568}
569
570impl BlockOperationHeader {
571    fn from_reader(reader: &block_operation_header::Reader) -> BlockOperationHeader {
572        BlockOperationHeader {
573            operation_id: reader.get_operation_id(),
574            data_offset: reader.get_data_offset(),
575            data_size: reader.get_data_size(),
576        }
577    }
578
579    fn copy_into_builder(&self, builder: &mut block_operation_header::Builder) {
580        builder.set_operation_id(self.operation_id);
581        builder.set_data_size(self.data_size);
582        builder.set_data_offset(self.data_offset);
583    }
584
585    fn read_frame<'a>(
586        &self,
587        operations_data: &'a [u8],
588    ) -> Result<crate::operation::OperationFrame<&'a [u8]>, Error> {
589        let offset_from = self.data_offset as usize;
590        let offset_to = self.data_offset as usize + self.data_size as usize;
591
592        let frame =
593            crate::operation::read_operation_frame(&operations_data[offset_from..offset_to])?;
594
595        Ok(frame)
596    }
597}
598
599/// Represents signatures stored in a block. Since a node writes a block as soon
600/// as it has enough signatures, signatures can differ from one node to the
601/// other. Signatures frame is pre-allocated, which means that not all
602/// signatures may fit. But in theory, it should always contain enough space for
603/// all nodes to add their own signature.
604pub struct BlockSignatures {
605    signatures: Vec<BlockSignature>,
606}
607
608impl BlockSignatures {
609    pub fn new_from_signatures(signatures: Vec<BlockSignature>) -> BlockSignatures {
610        BlockSignatures { signatures }
611    }
612
613    /// Create signatures with pre-allocated space for the number of nodes we
614    /// have in the cell
615    pub fn empty_signatures_for_nodes(cell: &Cell) -> BlockSignatures {
616        let nodes = cell.nodes();
617        let signatures = nodes
618            .iter()
619            .all()
620            .filter(|cn| cn.has_role(CellNodeRole::Chain))
621            .map(|cell_node| BlockSignature {
622                node_id: cell_node.node().id().clone(),
623                signature: Signature::empty(),
624            })
625            .collect();
626
627        BlockSignatures { signatures }
628    }
629
630    fn to_frame_builder(&self) -> CapnpFrameBuilder<block_signatures::Owned> {
631        let mut frame_builder = CapnpFrameBuilder::new();
632
633        let signatures_builder: block_signatures::Builder = frame_builder.get_builder();
634        let mut signatures_array = signatures_builder.init_signatures(self.signatures.len() as u32);
635        for (i, signature) in self.signatures.iter().enumerate() {
636            let mut signature_builder = signatures_array.reborrow().get(i as u32);
637            signature.copy_into_builder(&mut signature_builder);
638        }
639
640        frame_builder
641    }
642
643    pub fn to_frame_for_new_block(
644        &self,
645        operations_size: BlockOperationsSize,
646    ) -> Result<SignaturesFrame<Bytes>, Error> {
647        let mut signatures_frame_builder = self.to_frame_builder();
648        let mut signatures_builder = signatures_frame_builder.get_builder();
649        signatures_builder.set_operations_size(operations_size);
650
651        let frame_builder =
652            SizedFrameBuilder::new(PaddedFrameBuilder::new(signatures_frame_builder, 0));
653        let frame_data = frame_builder.as_bytes();
654        Self::read_frame(frame_data)
655    }
656
657    pub fn to_frame_for_existing_block(
658        &self,
659        header_reader: &block_header::Reader,
660    ) -> Result<SignaturesFrame<Bytes>, Error> {
661        let expected_signatures_size = usize::from(header_reader.get_signatures_size());
662
663        // create capnp frame
664        let mut signatures_frame_builder = self.to_frame_builder();
665        let mut signatures_builder = signatures_frame_builder.get_builder();
666        signatures_builder.set_operations_size(header_reader.get_operations_size());
667        let signatures_frame_data = signatures_frame_builder.as_bytes();
668        let signatures_frame_data_len = signatures_frame_data.len();
669
670        // create the enclosure frame (sized & padded)
671        let mut frame_builder =
672            SizedFrameBuilder::new(PaddedFrameBuilder::new(signatures_frame_data, 0));
673        let frame_expected_size = frame_builder
674            .expected_size()
675            .expect("Frame should had been sized");
676
677        // check if we need to add padding to match original signatures size
678        if frame_expected_size < expected_signatures_size {
679            let diff = expected_signatures_size - frame_expected_size;
680            frame_builder
681                .inner_mut()
682                .set_minimum_size(signatures_frame_data_len + diff);
683        }
684
685        // we build the frame and re-read it
686        let signatures_frame = Self::read_frame(frame_builder.as_bytes())?;
687
688        // make sure that the signatures frame size is the same as the one in block
689        // header
690        if signatures_frame.whole_data_size() != expected_signatures_size {
691            return Err(Error::Integrity(format!(
692                "Block local signatures isn't the same size as expected (this={} expected={})",
693                signatures_frame.whole_data_size(),
694                header_reader.get_signatures_size()
695            )));
696        }
697
698        Ok(signatures_frame)
699    }
700
701    pub fn read_frame<I: FrameReader>(inner: I) -> Result<SignaturesFrame<I>, Error> {
702        let sized_frame = SizedFrame::new(inner)?;
703        let padded_frame = PaddedFrame::new(sized_frame)?;
704        let frame = TypedCapnpFrame::new(padded_frame)?;
705        Ok(frame)
706    }
707
708    pub fn read_frame_from_next_offset<I: FrameReader>(
709        inner: I,
710        next_offset: usize,
711    ) -> Result<SignaturesFrame<I>, Error> {
712        let sized_frame = SizedFrame::new_from_next_offset(inner, next_offset)?;
713        let padded_frame = PaddedFrame::new(sized_frame)?;
714        let frame = TypedCapnpFrame::new(padded_frame)?;
715        Ok(frame)
716    }
717}
718
719/// Represents a signature of the block by one node, using its own key to sign
720/// the block's hash.
721pub struct BlockSignature {
722    pub node_id: NodeId,
723    pub signature: Signature,
724}
725
726impl BlockSignature {
727    pub fn new(node_id: NodeId, signature: Signature) -> BlockSignature {
728        BlockSignature { node_id, signature }
729    }
730
731    pub fn copy_into_builder(&self, builder: &mut block_signature::Builder) {
732        builder.set_node_id(self.node_id.to_string().as_str());
733        builder.set_node_signature(self.signature.get_bytes());
734    }
735}
736
737/// Block related errors
738#[derive(Debug, thiserror::Error)]
739pub enum Error {
740    #[error("Block integrity error: {0}")]
741    Integrity(String),
742
743    #[error("An offset is out of the block data: {0}")]
744    OutOfBound(String),
745
746    #[error("Operations related error: {0}")]
747    Operation(#[from] crate::operation::Error),
748
749    #[error("Framing error: {0}")]
750    Framing(#[from] exocore_core::framing::Error),
751
752    #[error("Error in capnp serialization: {0}")]
753    Serialization(#[from] capnp::Error),
754
755    #[error("Field is not in capnp schema: code={0}")]
756    SerializationNotInSchema(u16),
757
758    #[error("Other operation error: {0}")]
759    Other(String),
760}
761
762impl From<capnp::NotInSchema> for Error {
763    fn from(err: capnp::NotInSchema) -> Self {
764        Error::SerializationNotInSchema(err.0)
765    }
766}
767
768#[cfg(test)]
769mod tests {
770    use exocore_core::{
771        cell::{FullCell, LocalNode, Node},
772        framing::FrameReader,
773    };
774
775    use super::*;
776    use crate::{
777        block::{Block, BlockBuilder, BlockOperations},
778        data::RefData,
779        operation::OperationBuilder,
780    };
781
782    #[test]
783    fn block_create_and_read() -> anyhow::Result<()> {
784        let local_node = LocalNode::generate();
785        let full_cell = FullCell::generate(local_node.clone())?;
786
787        {
788            // local node is chain node
789            let mut nodes = full_cell.cell().nodes_mut();
790            let local_cell_node = nodes.get_mut(local_node.id()).unwrap();
791            local_cell_node.add_role(CellNodeRole::Chain);
792
793            // second node is not chain node
794            nodes.add(Node::generate_temporary());
795        }
796
797        let genesis = BlockBuilder::build_genesis(&full_cell)?;
798
799        let operations = vec![
800            OperationBuilder::new_entry(123, local_node.id(), b"some_data")
801                .sign_and_build(&local_node)?
802                .frame,
803        ];
804        let operations = BlockOperations::from_operations(operations.into_iter())?;
805
806        let second_block =
807            BlockBuilder::build_with_prev_block(full_cell.cell(), &genesis, 0, operations)?;
808
809        let mut data = [0u8; 5000];
810        second_block.copy_data_into(&mut data);
811
812        let block_data = RefData::new(&data[0..second_block.total_size()]);
813        let read_second_block = DataBlock::new(block_data)?;
814        assert_eq!(
815            second_block.header.whole_data(),
816            read_second_block.header.whole_data()
817        );
818
819        assert_eq!(
820            second_block.operations_data.as_ref(),
821            read_second_block.operations_data.slice(..),
822        );
823        assert_eq!(
824            second_block.signatures.whole_data(),
825            read_second_block.signatures.whole_data()
826        );
827
828        let header_reader = second_block.header.get_reader()?;
829        assert_eq!(header_reader.get_offset(), genesis.next_offset());
830        assert_eq!(
831            header_reader.get_signatures_size(),
832            second_block.signatures.whole_data_size() as u16
833        );
834        assert_eq!(
835            header_reader.get_operations_size(),
836            second_block.operations_data.len() as u32
837        );
838
839        let signatures_reader = second_block.signatures.get_reader()?;
840        assert_eq!(
841            signatures_reader.get_operations_size(),
842            second_block.operations_data.len() as u32
843        );
844
845        // 1 signature only since only our nodes is chain node
846        let signatures = signatures_reader.get_signatures()?;
847        assert_eq!(signatures.len(), 1);
848
849        Ok(())
850    }
851
852    #[test]
853    fn block_operations() -> anyhow::Result<()> {
854        let local_node = LocalNode::generate();
855        let full_cell = FullCell::generate(local_node.clone())?;
856        let genesis = BlockBuilder::build_genesis(&full_cell)?;
857
858        // 0 operations
859        let block = BlockBuilder::build_with_prev_block(
860            full_cell.cell(),
861            &genesis,
862            0,
863            BlockOperations::empty(),
864        )?;
865        assert_eq!(block.operations_iter()?.count(), 0);
866
867        // 5 operations
868        let operations = (0..5).map(|i| {
869            OperationBuilder::new_entry(i, local_node.id(), b"op1")
870                .sign_and_build(&local_node)
871                .unwrap()
872                .frame
873        });
874
875        let block_operations = BlockOperations::from_operations(operations)?;
876        let block =
877            BlockBuilder::build_with_prev_block(full_cell.cell(), &genesis, 0, block_operations)?;
878        assert_eq!(block.operations_iter()?.count(), 5);
879
880        Ok(())
881    }
882
883    #[test]
884    fn should_allocate_signatures_space_for_nodes() -> anyhow::Result<()> {
885        let local_node = LocalNode::generate();
886        let full_cell = FullCell::generate(local_node.clone())?;
887        let cell = full_cell.cell();
888
889        let node2 = {
890            // local node is chain node
891            let mut nodes = cell.nodes_mut();
892            let local_cell_node = nodes.get_mut(local_node.id()).unwrap();
893            local_cell_node.add_role(CellNodeRole::Chain);
894
895            // second node is not chain node
896            let node2 = Node::generate_temporary();
897            nodes.add(node2.clone());
898            node2
899        };
900
901        let genesis_block = BlockBuilder::build_genesis(&full_cell)?;
902
903        // only first node is chain node
904        let block_ops = BlockOperations::empty();
905        let block1 = BlockBuilder::build_with_prev_block(cell, &genesis_block, 0, block_ops)?;
906        assert!(block1.signatures.whole_data_size() > 100);
907
908        // make second node chain node, should now have more signature size
909        {
910            let mut nodes = cell.nodes_mut();
911            let cell_node_2 = nodes.get_mut(node2.id()).unwrap();
912            cell_node_2.add_role(CellNodeRole::Chain);
913        }
914
915        let block_ops = BlockOperations::empty();
916        let block2 = BlockBuilder::build_with_prev_block(cell, &genesis_block, 0, block_ops)?;
917        assert!(block2.signatures.whole_data_size() > block1.signatures.whole_data_size());
918
919        Ok(())
920    }
921
922    #[test]
923    fn should_pad_signatures_from_block_signature_size() -> anyhow::Result<()> {
924        let local_node = LocalNode::generate();
925        let full_cell = FullCell::generate(local_node)?;
926        let cell = full_cell.cell();
927        let genesis_block = BlockBuilder::build_genesis(&full_cell)?;
928
929        let block_ops = BlockOperations::empty();
930        let block1 = BlockBuilder::build_with_prev_block(cell, &genesis_block, 0, block_ops)?;
931        let block1_reader: block_header::Reader = block1.header().get_reader()?;
932
933        // generate new signatures for existing block
934        let block_signatures = BlockSignatures::new_from_signatures(Vec::new());
935        let signatures_frame = block_signatures.to_frame_for_existing_block(&block1_reader)?;
936
937        // new signatures frame should be the same size as the signatures specified in
938        // block
939        assert_eq!(
940            usize::from(block1_reader.get_signatures_size()),
941            signatures_frame.whole_data_size()
942        );
943
944        Ok(())
945    }
946}