ckb_snapshot/
lib.rs

1//! Rocksdb snapshot wrapper
2
3use arc_swap::{ArcSwap, Guard};
4use ckb_chain_spec::{
5    consensus::{Consensus, ConsensusProvider},
6    versionbits::{DeploymentPos, ThresholdState, VersionbitsIndexer},
7};
8use ckb_db::{
9    DBPinnableSlice,
10    iter::{DBIter, IteratorMode},
11};
12use ckb_db_schema::Col;
13use ckb_freezer::Freezer;
14use ckb_merkle_mountain_range::{
15    Error as MMRError, MMRStore, Result as MMRResult, leaf_index_to_mmr_size,
16};
17use ckb_proposal_table::ProposalView;
18use ckb_store::{ChainStore, StoreCache, StoreSnapshot};
19use ckb_traits::{HeaderFields, HeaderFieldsProvider, HeaderProvider};
20use ckb_types::core::error::OutPointError;
21use ckb_types::{
22    U256,
23    core::{
24        BlockNumber, EpochExt, HeaderView, TransactionView, Version,
25        cell::{CellChecker, CellProvider, CellStatus, HeaderChecker},
26    },
27    packed::{Byte32, HeaderDigest, OutPoint},
28    utilities::merkle_mountain_range::ChainRootMMR,
29};
30use std::hash::{Hash, Hasher};
31use std::sync::Arc;
32
33/// An Atomic wrapper for Snapshot
34pub struct SnapshotMgr {
35    inner: ArcSwap<Snapshot>,
36}
37
38impl SnapshotMgr {
39    /// Create new SnapshotMgr
40    pub fn new(snapshot: Arc<Snapshot>) -> Self {
41        SnapshotMgr {
42            inner: ArcSwap::new(snapshot),
43        }
44    }
45
46    /// Provides a temporary borrow of snapshot
47    pub fn load(&self) -> Guard<Arc<Snapshot>> {
48        self.inner.load()
49    }
50
51    /// Replaces the snapshot inside this instance.
52    pub fn store(&self, snapshot: Arc<Snapshot>) {
53        self.inner.store(snapshot);
54    }
55}
56
57/// A snapshot captures a point-in-time view of the DB at the time it's created
58//
59//                   yes —— new snapshot
60//                   /                    \
61//    tip —— change?                        SnapshotMgr swap
62//                  \                      /
63//                   no —— refresh snapshot
64pub struct Snapshot {
65    tip_header: HeaderView,
66    total_difficulty: U256,
67    epoch_ext: EpochExt,
68    store: StoreSnapshot,
69    proposals: ProposalView,
70    consensus: Arc<Consensus>,
71}
72
73impl Hash for Snapshot {
74    fn hash<H: Hasher>(&self, state: &mut H) {
75        Hash::hash(&self.tip_header, state);
76    }
77}
78
79impl PartialEq for Snapshot {
80    fn eq(&self, other: &Self) -> bool {
81        self.tip_header == other.tip_header
82    }
83}
84
85impl Eq for Snapshot {}
86
87impl ::std::fmt::Debug for Snapshot {
88    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
89        write!(f, "Snapshot {{ tip_hash: {} }}", self.tip_header.hash())
90    }
91}
92
93impl Snapshot {
94    /// New snapshot created after tip change
95    pub fn new(
96        tip_header: HeaderView,
97        total_difficulty: U256,
98        epoch_ext: EpochExt,
99        store: StoreSnapshot,
100        proposals: ProposalView,
101        consensus: Arc<Consensus>,
102    ) -> Snapshot {
103        Snapshot {
104            tip_header,
105            total_difficulty,
106            epoch_ext,
107            store,
108            proposals,
109            consensus,
110        }
111    }
112
113    /// Refreshing on block commit is necessary operation, even tip remains unchanged.
114    /// when node relayed compact block,if some uncles were not available from receiver's local sources,
115    /// in GetBlockTransactions/BlockTransactions roundtrip, node will need access block data of uncles.
116    pub fn refresh(&self, store: StoreSnapshot) -> Snapshot {
117        Snapshot {
118            store,
119            tip_header: self.tip_header.clone(),
120            total_difficulty: self.total_difficulty.clone(),
121            epoch_ext: self.epoch_ext.clone(),
122            proposals: self.proposals.clone(),
123            consensus: Arc::clone(&self.consensus),
124        }
125    }
126
127    /// Return reference of tip header
128    pub fn tip_header(&self) -> &HeaderView {
129        &self.tip_header
130    }
131
132    /// Return tip header number
133    pub fn tip_number(&self) -> BlockNumber {
134        self.tip_header.number()
135    }
136
137    /// Return tip header hash
138    pub fn tip_hash(&self) -> Byte32 {
139        self.tip_header.hash()
140    }
141
142    /// Return current epoch information
143    pub fn epoch_ext(&self) -> &EpochExt {
144        &self.epoch_ext
145    }
146
147    /// Return reference of `Consensus`
148    pub fn consensus(&self) -> &Consensus {
149        &self.consensus
150    }
151
152    /// Makes a clone of the `Arc<Consensus>`
153    pub fn cloned_consensus(&self) -> Arc<Consensus> {
154        Arc::clone(&self.consensus)
155    }
156
157    /// Return reference of proposals view
158    pub fn proposals(&self) -> &ProposalView {
159        &self.proposals
160    }
161
162    /// Return current best chain total_difficulty
163    pub fn total_difficulty(&self) -> &U256 {
164        &self.total_difficulty
165    }
166
167    /// Returns what version a new block should use.
168    pub fn compute_versionbits(&self, parent: &HeaderView) -> Option<Version> {
169        self.consensus.compute_versionbits(parent, self)
170    }
171
172    /// Returns specified softfork active or not
173    pub fn versionbits_active(&self, pos: DeploymentPos) -> bool {
174        self.consensus
175            .versionbits_state(pos, &self.tip_header, self)
176            .map(|state| state == ThresholdState::Active)
177            .unwrap_or(false)
178    }
179
180    /// Returns the chain root MMR for a provided block.
181    pub fn chain_root_mmr(&self, block_number: BlockNumber) -> ChainRootMMR<&Self> {
182        let mmr_size = leaf_index_to_mmr_size(block_number);
183        ChainRootMMR::new(mmr_size, self)
184    }
185}
186
187impl ChainStore for Snapshot {
188    fn cache(&self) -> Option<&StoreCache> {
189        self.store.cache()
190    }
191
192    fn get(&self, col: Col, key: &[u8]) -> Option<DBPinnableSlice> {
193        self.store.get(col, key)
194    }
195
196    fn freezer(&self) -> Option<&Freezer> {
197        self.store.freezer()
198    }
199
200    fn get_iter(&self, col: Col, mode: IteratorMode) -> DBIter {
201        self.store.get_iter(col, mode)
202    }
203
204    fn get_tip_header(&self) -> Option<HeaderView> {
205        Some(self.tip_header.clone())
206    }
207
208    fn get_current_epoch_ext(&self) -> Option<EpochExt> {
209        Some(self.epoch_ext.clone())
210    }
211}
212
213impl VersionbitsIndexer for Snapshot {
214    fn block_epoch_index(&self, block_hash: &Byte32) -> Option<Byte32> {
215        ChainStore::get_block_epoch_index(self, block_hash)
216    }
217
218    fn epoch_ext(&self, index: &Byte32) -> Option<EpochExt> {
219        ChainStore::get_epoch_ext(self, index)
220    }
221
222    fn block_header(&self, block_hash: &Byte32) -> Option<HeaderView> {
223        ChainStore::get_block_header(self, block_hash)
224    }
225
226    fn cellbase(&self, block_hash: &Byte32) -> Option<TransactionView> {
227        ChainStore::get_cellbase(self, block_hash)
228    }
229}
230
231impl CellProvider for Snapshot {
232    fn cell(&self, out_point: &OutPoint, eager_load: bool) -> CellStatus {
233        match self.get_cell(out_point) {
234            Some(mut cell_meta) => {
235                if eager_load {
236                    if let Some((data, data_hash)) = self.get_cell_data(out_point) {
237                        cell_meta.mem_cell_data = Some(data);
238                        cell_meta.mem_cell_data_hash = Some(data_hash);
239                    }
240                }
241                CellStatus::live_cell(cell_meta)
242            }
243            None => CellStatus::Unknown,
244        }
245    }
246}
247
248impl CellChecker for Snapshot {
249    fn is_live(&self, out_point: &OutPoint) -> Option<bool> {
250        if self.have_cell(out_point) {
251            Some(true)
252        } else {
253            None
254        }
255    }
256}
257
258impl HeaderChecker for Snapshot {
259    fn check_valid(&self, block_hash: &Byte32) -> Result<(), OutPointError> {
260        if !self.is_main_chain(block_hash) {
261            return Err(OutPointError::InvalidHeader(block_hash.clone()));
262        }
263        ChainStore::get_block_header(self, block_hash)
264            .ok_or_else(|| OutPointError::InvalidHeader(block_hash.clone()))?;
265        Ok(())
266    }
267}
268
269impl HeaderProvider for Snapshot {
270    fn get_header(&self, hash: &Byte32) -> Option<HeaderView> {
271        self.store.get_block_header(hash)
272    }
273}
274
275impl HeaderFieldsProvider for Snapshot {
276    fn get_header_fields(&self, hash: &Byte32) -> Option<HeaderFields> {
277        self.store
278            .get_block_header(hash)
279            .map(|header| HeaderFields {
280                hash: header.hash(),
281                number: header.number(),
282                epoch: header.epoch(),
283                timestamp: header.timestamp(),
284                parent_hash: header.parent_hash(),
285            })
286    }
287}
288
289impl ConsensusProvider for Snapshot {
290    fn get_consensus(&self) -> &Consensus {
291        self.consensus()
292    }
293}
294
295impl MMRStore<HeaderDigest> for &Snapshot {
296    fn get_elem(&self, pos: u64) -> MMRResult<Option<HeaderDigest>> {
297        Ok(self.store.get_header_digest(pos))
298    }
299
300    fn append(&mut self, _pos: u64, _elems: Vec<HeaderDigest>) -> MMRResult<()> {
301        Err(MMRError::StoreError(
302            "Failed to append to MMR, snapshot MMR is readonly".into(),
303        ))
304    }
305}