chainseeker_server/db/
block.rs1use 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 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 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}