zcash_client_sqlite/
testing.rs1use prost::Message;
7use rusqlite::params;
8
9use zcash_client_backend::{
10 data_api::testing::{CacheInsertionResult, NoteCommitments, TestCache},
11 proto::compact_formats::CompactBlock,
12};
13use zcash_protocol::TxId;
14#[cfg(feature = "orchard")]
15use {
16 shardtree::{error::ShardTreeError, store::ShardStore},
17 zcash_client_backend::data_api::WalletCommitmentTrees,
18 zcash_protocol::consensus::BlockHeight,
19};
20
21use crate::{chain::init::init_cache_database, error::SqliteClientError};
22
23use super::BlockDb;
24
25#[cfg(all(test, feature = "unstable"))]
26#[cfg(all(test, feature = "unstable"))]
27use std::io::Write;
28#[cfg(all(test, feature = "unstable"))]
29use {
30 crate::{
31 FsBlockDb, FsBlockDbError,
32 chain::{BlockMeta, init::init_blockmeta_db},
33 },
34 std::fs::File,
35 tempfile::TempDir,
36};
37
38pub mod db;
39#[cfg(test)]
43pub(crate) mod pool;
44
45pub struct BlockCache {
48 db_cache: BlockDb,
49}
50
51impl BlockCache {
52 pub fn new() -> Self {
54 let db_cache = BlockDb::from_connection(rusqlite::Connection::open_in_memory().unwrap());
55 init_cache_database(&db_cache).unwrap();
56
57 BlockCache { db_cache }
58 }
59}
60
61impl Default for BlockCache {
62 fn default() -> Self {
63 Self::new()
64 }
65}
66
67pub struct BlockCacheInsertionResult {
70 txids: Vec<TxId>,
71 #[allow(dead_code)]
72 note_commitments: NoteCommitments,
73}
74
75impl BlockCacheInsertionResult {
76 #[allow(dead_code)]
77 pub(crate) fn note_commitments(&self) -> &NoteCommitments {
78 &self.note_commitments
79 }
80}
81
82impl CacheInsertionResult for BlockCacheInsertionResult {
83 fn txids(&self) -> &[TxId] {
84 &self.txids[..]
85 }
86}
87
88impl TestCache for BlockCache {
89 type BsError = SqliteClientError;
90 type BlockSource = BlockDb;
91 type InsertResult = BlockCacheInsertionResult;
92
93 fn block_source(&self) -> &Self::BlockSource {
94 &self.db_cache
95 }
96
97 fn insert(&mut self, cb: &CompactBlock) -> Self::InsertResult {
98 let cb_bytes = cb.encode_to_vec();
99 let note_commitments = NoteCommitments::from_compact_block(cb);
100 self.db_cache
101 .0
102 .execute(
103 "INSERT INTO compactblocks (height, data) VALUES (?, ?)",
104 params![u32::from(cb.height()), cb_bytes,],
105 )
106 .unwrap();
107
108 BlockCacheInsertionResult {
109 txids: cb.vtx.iter().map(|tx| tx.txid()).collect(),
110 note_commitments,
111 }
112 }
113
114 fn truncate_to_height(&mut self, height: zcash_protocol::consensus::BlockHeight) {
115 self.db_cache
116 .0
117 .execute(
118 "DELETE FROM compactblocks WHERE height > ?",
119 params![u32::from(height)],
120 )
121 .unwrap();
122 }
123}
124
125#[cfg(feature = "orchard")]
130pub fn highest_rooted_orchard_checkpoint<W>(db: &mut W, from: BlockHeight) -> Option<BlockHeight>
131where
132 W: zcash_client_backend::data_api::WalletCommitmentTrees,
133{
134 db.with_orchard_tree_mut::<_, _, ShardTreeError<<W as WalletCommitmentTrees>::Error>>(|tree| {
135 let store = tree.store();
138 let count = store.checkpoint_count().map_err(ShardTreeError::Storage)?;
139 let mut highest: Option<BlockHeight> = None;
140 store
141 .for_each_checkpoint(count, |id, _| {
142 if *id <= from {
143 highest = Some(highest.map_or(*id, |h| h.max(*id)));
144 }
145 Ok(())
146 })
147 .map_err(ShardTreeError::Storage)?;
148 Ok(highest)
149 })
150 .expect("queries the Orchard tree")
151}
152
153#[cfg(all(test, feature = "unstable"))]
154pub(crate) struct FsBlockCache {
155 fsblockdb_root: TempDir,
156 db_meta: FsBlockDb,
157}
158
159#[cfg(all(test, feature = "unstable"))]
160impl FsBlockCache {
161 pub(crate) fn new() -> Self {
162 let fsblockdb_root = tempfile::tempdir().unwrap();
163 let mut db_meta = FsBlockDb::for_path(&fsblockdb_root).unwrap();
164 init_blockmeta_db(&mut db_meta).unwrap();
165
166 FsBlockCache {
167 fsblockdb_root,
168 db_meta,
169 }
170 }
171}
172
173#[cfg(all(test, feature = "unstable"))]
176#[derive(Debug)]
177pub struct FsBlockCacheInsertionResult {
178 txids: Vec<TxId>,
179 pub(crate) block_meta: BlockMeta,
180}
181
182#[cfg(all(test, feature = "unstable"))]
183impl CacheInsertionResult for FsBlockCacheInsertionResult {
184 fn txids(&self) -> &[TxId] {
185 &self.txids[..]
186 }
187}
188
189#[cfg(all(test, feature = "unstable"))]
190impl TestCache for FsBlockCache {
191 type BsError = FsBlockDbError;
192 type BlockSource = FsBlockDb;
193 type InsertResult = FsBlockCacheInsertionResult;
194
195 fn block_source(&self) -> &Self::BlockSource {
196 &self.db_meta
197 }
198
199 fn insert(&mut self, cb: &CompactBlock) -> Self::InsertResult {
200 let txids = cb.vtx.iter().map(|tx| tx.txid()).collect();
201 let block_meta = BlockMeta {
202 height: cb.height(),
203 block_hash: cb.hash(),
204 block_time: cb.time,
205 sapling_outputs_count: cb.vtx.iter().map(|tx| tx.outputs.len() as u32).sum(),
206 orchard_actions_count: cb.vtx.iter().map(|tx| tx.actions.len() as u32).sum(),
207 };
208
209 let blocks_dir = self.fsblockdb_root.as_ref().join("blocks");
210 let block_path = block_meta.block_file_path(&blocks_dir);
211
212 File::create(block_path)
213 .unwrap()
214 .write_all(&cb.encode_to_vec())
215 .unwrap();
216
217 FsBlockCacheInsertionResult { txids, block_meta }
218 }
219
220 fn truncate_to_height(&mut self, height: zcash_protocol::consensus::BlockHeight) {
221 self.db_meta.truncate_to_height(height).unwrap()
222 }
223}