1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use std::ops::Range;

use crate::{
    block::{Block, BlockOffset, DataBlock},
    operation::OperationId,
};

#[cfg(feature = "directory-chain")]
pub mod directory;
pub mod error;
pub use error::Error;
pub mod data;
pub use data::ChainData;

/// Persistence for the chain
pub trait ChainStore: Send + Sync + 'static {
    fn segments(&self) -> Segments;

    fn write_block<B: Block>(&mut self, block: &B) -> Result<BlockOffset, Error>;

    fn blocks_iter(&self, from_offset: BlockOffset) -> StoredBlockIterator;

    fn blocks_iter_reverse(&self, from_next_offset: BlockOffset) -> StoredBlockIterator;

    fn get_block(&self, offset: BlockOffset) -> Result<DataBlock<ChainData>, Error>;

    fn get_block_from_next_offset(
        &self,
        next_offset: BlockOffset,
    ) -> Result<DataBlock<ChainData>, Error>;

    fn get_last_block(&self) -> Result<Option<DataBlock<ChainData>>, Error>;

    fn get_block_by_operation_id(
        &self,
        operation_id: OperationId,
    ) -> Result<Option<DataBlock<ChainData>>, Error>;

    fn truncate_from_offset(&mut self, offset: BlockOffset) -> Result<(), Error>;
}

/// Segment of the chain with a specified offsets range, in bytes.
///
/// The upper range is exclusive. You can use `get_block_from_next_offset` to
/// get the last block of the segment.
#[derive(Clone, Debug, PartialEq)]
pub struct Segment {
    pub range: Range<BlockOffset>,
}

/// Collection of segments of the chain.
#[derive(Clone, Debug, PartialEq)]
pub struct Segments(pub Vec<Segment>);

impl Segments {
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    pub fn len(&self) -> usize {
        self.0.len()
    }

    pub fn iter(&self) -> impl Iterator<Item = &Segment> {
        self.0.iter()
    }

    pub fn filter_in_range(
        self,
        from_offset: Option<BlockOffset>,
        to_offset: Option<BlockOffset>,
    ) -> Segments {
        let from_offset = from_offset.unwrap_or(std::u64::MIN);
        let to_offset = to_offset.unwrap_or(std::u64::MAX);

        Segments(
            self.0
                .into_iter()
                .filter(|segment| {
                    from_offset <= segment.range.end && segment.range.start <= to_offset
                })
                .collect(),
        )
    }
}

impl std::iter::IntoIterator for Segments {
    type Item = Segment;

    type IntoIter = std::vec::IntoIter<Segment>;

    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}

/// Iterator over stored blocks.
type StoredBlockIterator<'p> = Box<dyn Iterator<Item = Result<DataBlock<ChainData>, Error>> + 'p>;

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn segments_filter_in_range() {
        let segments = Segments(vec![
            Segment { range: 0..100 },
            Segment { range: 100..200 },
            Segment { range: 200..300 },
        ]);

        let assert_segments = |from, to, segs: Vec<BlockOffset>| {
            let matching = segments.clone().filter_in_range(from, to);
            let ids: Vec<BlockOffset> = matching.into_iter().map(|r| r.range.start / 100).collect();
            assert_eq!(segs, ids);
        };

        assert_segments(None, None, vec![0, 1, 2]);
        assert_segments(Some(0), None, vec![0, 1, 2]);
        assert_segments(None, Some(300), vec![0, 1, 2]);
        assert_segments(None, Some(299), vec![0, 1, 2]);
        assert_segments(None, Some(199), vec![0, 1]);
        assert_segments(Some(100), Some(199), vec![0, 1]);
        assert_segments(Some(101), Some(199), vec![1]);
    }
}