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
use core::fmt;
use std::fmt::Debug;
use std::fmt::Formatter;
use std::path::PathBuf;

use bitcoin::bip158::BlockFilter;
use kv::Bucket;
use kv::Config;
use kv::Integer;

use crate::BlockFilterStore;

/// Stores the block filters insinde a kv database
#[derive(Clone)]
pub struct KvFilterStore {
    bucket: Bucket<'static, Integer, Vec<u8>>,
}

impl Debug for KvFilterStore {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        f.debug_struct("KvFilterStore").finish()
    }
}

impl KvFilterStore {
    /// Creates a new [KvFilterStore] that stores it's content in `datadir`.
    ///
    /// If the path does't exist it'll be created. This store uses compression by default, if you
    /// want to make more granular configuration over the underlying Kv database, use `with_config`
    /// instead.
    pub fn new(datadir: &PathBuf) -> Self {
        let store = kv::Store::new(kv::Config {
            path: datadir.to_owned(),
            temporary: false,
            use_compression: false,
            flush_every_ms: None,
            cache_capacity: None,
            segment_size: None,
        })
        .expect("Could not open store");

        let bucket = store.bucket(Some("cfilters")).unwrap();
        KvFilterStore { bucket }
    }

    /// Creates a new [KvFilterStore] that stores it's content with a given config
    pub fn with_config(config: Config) -> Self {
        let store = kv::Store::new(config).expect("Could not open database");
        let bucket = store.bucket(Some("cffilters")).unwrap();
        KvFilterStore { bucket }
    }
}

impl BlockFilterStore for KvFilterStore {
    fn get_filter(&self, block_height: u32) -> Option<BlockFilter> {
        let value = self
            .bucket
            .get(&Integer::from(block_height))
            .ok()
            .flatten()?;

        Some(BlockFilter::new(&value))
    }

    fn put_filter(&self, block_height: u32, block_filter: BlockFilter) {
        self.bucket
            .set(&Integer::from(block_height), &block_filter.content)
            .expect("Bucket should be open");
    }

    fn get_height(&self) -> Option<u32> {
        // A bit of a hack to avoid opening a new bucket just for the height
        // write the height as the 0th block
        self.bucket
            .get(&Integer::from(0))
            .ok()
            .flatten()
            .map(|height| {
                let mut _height = [0u8; 4];
                _height.copy_from_slice(&height);
                u32::from_le_bytes(_height)
            })
    }

    fn put_height(&self, height: u32) {
        self.bucket
            .set(&Integer::from(0), &height.to_le_bytes().to_vec())
            .expect("Bucket should be open");
    }
}