1use 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
33pub struct SnapshotMgr {
35 inner: ArcSwap<Snapshot>,
36}
37
38impl SnapshotMgr {
39 pub fn new(snapshot: Arc<Snapshot>) -> Self {
41 SnapshotMgr {
42 inner: ArcSwap::new(snapshot),
43 }
44 }
45
46 pub fn load(&self) -> Guard<Arc<Snapshot>> {
48 self.inner.load()
49 }
50
51 pub fn store(&self, snapshot: Arc<Snapshot>) {
53 self.inner.store(snapshot);
54 }
55}
56
57pub 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 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 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 pub fn tip_header(&self) -> &HeaderView {
129 &self.tip_header
130 }
131
132 pub fn tip_number(&self) -> BlockNumber {
134 self.tip_header.number()
135 }
136
137 pub fn tip_hash(&self) -> Byte32 {
139 self.tip_header.hash()
140 }
141
142 pub fn epoch_ext(&self) -> &EpochExt {
144 &self.epoch_ext
145 }
146
147 pub fn consensus(&self) -> &Consensus {
149 &self.consensus
150 }
151
152 pub fn cloned_consensus(&self) -> Arc<Consensus> {
154 Arc::clone(&self.consensus)
155 }
156
157 pub fn proposals(&self) -> &ProposalView {
159 &self.proposals
160 }
161
162 pub fn total_difficulty(&self) -> &U256 {
164 &self.total_difficulty
165 }
166
167 pub fn compute_versionbits(&self, parent: &HeaderView) -> Option<Version> {
169 self.consensus.compute_versionbits(parent, self)
170 }
171
172 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 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}