chainseeker_server/db/
block.rs

1use bitcoin::{Txid, Block, BlockHeader, BlockHash};
2use bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR;
3
4use crate::*;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct BlockHashDBValue {
8    pub block_hash: BlockHash,
9}
10
11impl Serialize for BlockHashDBValue {
12    fn serialize(&self) -> Vec<u8> {
13        consensus_encode(&self.block_hash)
14    }
15}
16
17impl Deserialize for BlockHashDBValue {
18    fn deserialize(buf: &[u8]) -> Self {
19        let block_hash = consensus_decode(buf);
20        Self {
21            block_hash,
22        }
23    }
24}
25
26#[derive(Debug)]
27pub struct BlockHashDB {
28    /// Stores (block_height, block_hash).
29    db: RocksDB<u32, BlockHashDBValue>,
30}
31
32impl BlockHashDB {
33    pub fn path(coin: &str) -> String {
34        format!("{}/{}/block_hash", data_dir(), coin)
35    }
36    pub fn new(coin: &str, temporary: bool) -> Self {
37        let path = Self::path(coin);
38        Self {
39            db: RocksDB::new(&path, temporary),
40        }
41    }
42    pub fn put(&self, height: u32, block: &Block) {
43        self.db.put(&height, &BlockHashDBValue { block_hash: block.block_hash() });
44    }
45    pub fn get(&self, height: u32) -> Option<BlockHash> {
46        self.db.get(&height).map(|value| value.block_hash)
47    }
48}
49
50#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct BlockContentDBValue {
52    pub height: u32,
53    pub block_header: BlockHeader,
54    pub size: u32,
55    pub strippedsize: u32,
56    pub weight: u32,
57    pub txids: Vec<Txid>,
58}
59
60impl BlockContentDBValue {
61    pub fn new(height: u32, block: &Block) -> Self {
62        let size = block.get_size() as u32;
63        let weight = block.get_weight() as u32;
64        Self {
65            height,
66            block_header: block.header,
67            size,
68            // TODO: wating for upstream merge.
69            //strippedsize: block.get_strippedsize() as u32,
70            strippedsize: (weight - size) / ((WITNESS_SCALE_FACTOR - 1) as u32),
71            weight,
72            txids: block.txdata.iter().map(|tx| tx.txid()).collect(),
73        }
74    }
75}
76
77const BLOCK_HEADER_LEN: usize = 80;
78
79impl Serialize for BlockContentDBValue {
80    fn serialize(&self) -> Vec<u8> {
81        let mut ret = vec![
82            self.height.to_le_bytes().to_vec(),
83            consensus_encode(&self.block_header),
84            self.size.to_le_bytes().to_vec(),
85            self.strippedsize.to_le_bytes().to_vec(),
86            self.weight.to_le_bytes().to_vec(),
87        ];
88        for txid in self.txids.iter() {
89            ret.push(consensus_encode(txid));
90        }
91        ret.concat()
92    }
93}
94
95impl Deserialize for BlockContentDBValue {
96    fn deserialize(buf: &[u8]) -> Self {
97        let height = bytes_to_u32(&buf[0..4]);
98        let mut offset = 4usize;
99        let block_header = consensus_decode(&buf[offset..BLOCK_HEADER_LEN+offset]);
100        offset += BLOCK_HEADER_LEN;
101        let size = bytes_to_u32(&buf[offset..offset+4]);
102        offset += 4;
103        let strippedsize = bytes_to_u32(&buf[offset..offset+4]);
104        offset += 4;
105        let weight = bytes_to_u32(&buf[offset..offset+4]);
106        offset += 4;
107        let mut txids = Vec::new();
108        while offset < buf.len() {
109            txids.push(consensus_decode(&buf[offset..offset+32]));
110            offset += 32;
111        }
112        Self {
113            height,
114            block_header,
115            size,
116            strippedsize,
117            weight,
118            txids,
119        }
120    }
121}
122
123#[derive(Debug)]
124pub struct BlockContentDB {
125    db: RocksDB<BlockHashDBValue, BlockContentDBValue>,
126}
127
128impl BlockContentDB {
129    pub fn path(coin: &str) -> String {
130        format!("{}/{}/block", data_dir(), coin)
131    }
132    pub fn new(coin: &str, temporary: bool) -> Self {
133        let path = Self::path(coin);
134        Self {
135            db: RocksDB::new(&path, temporary),
136        }
137    }
138    pub fn put(&self, height: u32, block: &Block) {
139        self.db.put(&BlockHashDBValue { block_hash: block.block_hash() }, &BlockContentDBValue::new(height, &block));
140    }
141    pub fn get(&self, block_hash: &BlockHash) -> Option<BlockContentDBValue> {
142        self.db.get(&BlockHashDBValue { block_hash: *block_hash })
143    }
144}
145
146#[derive(Debug)]
147pub struct BlockDB {
148    hash_db: BlockHashDB,
149    content_db: BlockContentDB,
150}
151
152impl BlockDB {
153    pub fn new(coin: &str, temporary: bool) -> Self {
154        Self {
155            hash_db: BlockHashDB::new(coin, temporary),
156            content_db: BlockContentDB::new(coin, temporary),
157        }
158    }
159    pub fn put(&self, height: u32, block: &Block) {
160        self.hash_db.put(height, block);
161        self.content_db.put(height, block);
162    }
163    pub fn get(&self, height: u32) -> Option<BlockContentDBValue> {
164        let block_hash = match self.hash_db.get(height) {
165            Some(block_hash) => block_hash,
166            None => return None,
167        };
168        self.get_by_hash(&block_hash)
169    }
170    pub fn get_by_hash(&self, block_hash: &BlockHash) -> Option<BlockContentDBValue> {
171        self.content_db.get(block_hash)
172    }
173}
174
175#[cfg(test)]
176mod tests {
177    use super::*;
178    #[test]
179    fn put_and_get_block() {
180        let block_db = BlockDB::new("test/block", true);
181        let blocks = fixtures::regtest_blocks();
182        for (height, block) in blocks.iter().enumerate() {
183            block_db.put(height as u32, &block);
184        }
185        for (height, block) in blocks.iter().enumerate() {
186            assert_eq!(block_db.get(height as u32), Some(BlockContentDBValue::new(height as u32, &block)));
187        }
188        assert_eq!(block_db.get(blocks.len() as u32), None);
189    }
190}