Skip to main content

ckb_store/
store.rs

1use crate::cache::StoreCache;
2use crate::data_loader_wrapper::BorrowedDataLoaderWrapper;
3use ckb_db::{
4    DBPinnableSlice,
5    iter::{DBIter, Direction, IteratorMode},
6};
7use ckb_db_schema::{
8    COLUMN_BLOCK_BODY, COLUMN_BLOCK_EPOCH, COLUMN_BLOCK_EXT, COLUMN_BLOCK_EXTENSION,
9    COLUMN_BLOCK_FILTER, COLUMN_BLOCK_FILTER_HASH, COLUMN_BLOCK_HEADER, COLUMN_BLOCK_PROPOSAL_IDS,
10    COLUMN_BLOCK_UNCLE, COLUMN_CELL, COLUMN_CELL_DATA, COLUMN_CELL_DATA_HASH,
11    COLUMN_CHAIN_ROOT_MMR, COLUMN_EPOCH, COLUMN_INDEX, COLUMN_META, COLUMN_TRANSACTION_INFO,
12    COLUMN_UNCLES, Col, META_CURRENT_EPOCH_KEY, META_LATEST_BUILT_FILTER_DATA_KEY,
13    META_TIP_HEADER_KEY,
14};
15use ckb_freezer::Freezer;
16use ckb_types::{
17    bytes::Bytes,
18    core::{
19        BlockExt, BlockNumber, BlockView, EpochExt, EpochNumber, HeaderView, TransactionInfo,
20        TransactionView, UncleBlockVecView, cell::CellMeta,
21    },
22    packed::{self, OutPoint},
23    prelude::*,
24};
25
26/// The `ChainStore` trait provides chain data store interface
27pub trait ChainStore: Send + Sync + Sized {
28    /// Return cache reference
29    fn cache(&self) -> Option<&StoreCache>;
30    /// Return freezer reference
31    fn freezer(&self) -> Option<&Freezer>;
32    /// Return the bytes associated with a key value and the given column family.
33    fn get(&self, col: Col, key: &[u8]) -> Option<DBPinnableSlice<'_>>;
34    /// Return an iterator over the database key-value pairs in the given column family.
35    fn get_iter(&self, col: Col, mode: IteratorMode) -> DBIter<'_>;
36    /// Return the borrowed data loader wrapper
37    fn borrow_as_data_loader(&self) -> BorrowedDataLoaderWrapper<'_, Self> {
38        BorrowedDataLoaderWrapper::new(self)
39    }
40
41    /// Get block by block header hash
42    fn get_block(&self, h: &packed::Byte32) -> Option<BlockView> {
43        let header = self.get_block_header(h)?;
44        if let Some(freezer) = self.freezer()
45            && header.number() > 0
46            && header.number() < freezer.number()
47        {
48            let raw_block = freezer.retrieve(header.number()).expect("block frozen")?;
49            let raw_block = packed::BlockReader::from_compatible_slice(&raw_block)
50                .expect("checked data")
51                .to_entity();
52            return Some(raw_block.into_view());
53        }
54        let body = self.get_block_body(h);
55        let uncles = self
56            .get_block_uncles(h)
57            .expect("block uncles must be stored");
58        let proposals = self
59            .get_block_proposal_txs_ids(h)
60            .expect("block proposal_ids must be stored");
61        let extension_opt = self.get_block_extension(h);
62
63        let block = if let Some(extension) = extension_opt {
64            BlockView::new_unchecked_with_extension(header, uncles, body, proposals, extension)
65        } else {
66            BlockView::new_unchecked(header, uncles, body, proposals)
67        };
68        Some(block)
69    }
70
71    /// Get header by block header hash
72    fn get_block_header(&self, hash: &packed::Byte32) -> Option<HeaderView> {
73        if let Some(cache) = self.cache()
74            && let Some(header) = cache.headers.lock().get(hash)
75        {
76            return Some(header.clone());
77        };
78        let ret = self.get(COLUMN_BLOCK_HEADER, hash.as_slice()).map(|slice| {
79            let reader = packed::HeaderViewReader::from_slice_should_be_ok(slice.as_ref());
80            Into::<HeaderView>::into(reader)
81        });
82
83        if let Some(cache) = self.cache() {
84            ret.inspect(|header| {
85                cache.headers.lock().put(hash.clone(), header.clone());
86            })
87        } else {
88            ret
89        }
90    }
91
92    /// Get block body by block header hash
93    fn get_block_body(&self, hash: &packed::Byte32) -> Vec<TransactionView> {
94        let prefix = hash.as_slice();
95        self.get_iter(
96            COLUMN_BLOCK_BODY,
97            IteratorMode::From(prefix, Direction::Forward),
98        )
99        .take_while(|(key, _)| key.starts_with(prefix))
100        .map(|(_key, value)| {
101            let reader = packed::TransactionViewReader::from_slice_should_be_ok(value.as_ref());
102            Into::<TransactionView>::into(reader)
103        })
104        .collect()
105    }
106
107    /// Get unfrozen block from ky-store with given hash
108    fn get_unfrozen_block(&self, hash: &packed::Byte32) -> Option<BlockView> {
109        let header = self
110            .get(COLUMN_BLOCK_HEADER, hash.as_slice())
111            .map(|slice| {
112                let reader = packed::HeaderViewReader::from_slice_should_be_ok(slice.as_ref());
113                Into::<HeaderView>::into(reader)
114            })?;
115
116        let body = self.get_block_body(hash);
117
118        let uncles = self
119            .get(COLUMN_BLOCK_UNCLE, hash.as_slice())
120            .map(|slice| {
121                let reader =
122                    packed::UncleBlockVecViewReader::from_slice_should_be_ok(slice.as_ref());
123                Into::<UncleBlockVecView>::into(reader)
124            })
125            .expect("block uncles must be stored");
126
127        let proposals = self
128            .get(COLUMN_BLOCK_PROPOSAL_IDS, hash.as_slice())
129            .map(|slice| {
130                packed::ProposalShortIdVecReader::from_slice_should_be_ok(slice.as_ref())
131                    .to_entity()
132            })
133            .expect("block proposal_ids must be stored");
134
135        let extension_opt = self
136            .get(COLUMN_BLOCK_EXTENSION, hash.as_slice())
137            .map(|slice| packed::BytesReader::from_slice_should_be_ok(slice.as_ref()).to_entity());
138
139        let block = if let Some(extension) = extension_opt {
140            BlockView::new_unchecked_with_extension(header, uncles, body, proposals, extension)
141        } else {
142            BlockView::new_unchecked(header, uncles, body, proposals)
143        };
144
145        Some(block)
146    }
147
148    /// Get all transaction-hashes in block body by block header hash
149    fn get_block_txs_hashes(&self, hash: &packed::Byte32) -> Vec<packed::Byte32> {
150        if let Some(cache) = self.cache()
151            && let Some(hashes) = cache.block_tx_hashes.lock().get(hash)
152        {
153            return hashes.clone();
154        };
155
156        let prefix = hash.as_slice();
157        let ret: Vec<_> = self
158            .get_iter(
159                COLUMN_BLOCK_BODY,
160                IteratorMode::From(prefix, Direction::Forward),
161            )
162            .take_while(|(key, _)| key.starts_with(prefix))
163            .map(|(_key, value)| {
164                let reader = packed::TransactionViewReader::from_slice_should_be_ok(value.as_ref());
165                reader.hash().to_entity()
166            })
167            .collect();
168
169        if let Some(cache) = self.cache() {
170            cache.block_tx_hashes.lock().put(hash.clone(), ret.clone());
171        }
172
173        ret
174    }
175
176    /// Get proposal short id by block header hash
177    fn get_block_proposal_txs_ids(
178        &self,
179        hash: &packed::Byte32,
180    ) -> Option<packed::ProposalShortIdVec> {
181        if let Some(cache) = self.cache()
182            && let Some(data) = cache.block_proposals.lock().get(hash)
183        {
184            return Some(data.clone());
185        };
186
187        let ret = self
188            .get(COLUMN_BLOCK_PROPOSAL_IDS, hash.as_slice())
189            .map(|slice| {
190                packed::ProposalShortIdVecReader::from_slice_should_be_ok(slice.as_ref())
191                    .to_entity()
192            });
193
194        if let Some(cache) = self.cache() {
195            ret.inspect(|data| {
196                cache.block_proposals.lock().put(hash.clone(), data.clone());
197            })
198        } else {
199            ret
200        }
201    }
202
203    /// Get block uncles by block header hash
204    fn get_block_uncles(&self, hash: &packed::Byte32) -> Option<UncleBlockVecView> {
205        if let Some(cache) = self.cache()
206            && let Some(data) = cache.block_uncles.lock().get(hash)
207        {
208            return Some(data.clone());
209        };
210
211        let ret = self.get(COLUMN_BLOCK_UNCLE, hash.as_slice()).map(|slice| {
212            let reader = packed::UncleBlockVecViewReader::from_slice_should_be_ok(slice.as_ref());
213            Into::<UncleBlockVecView>::into(reader)
214        });
215
216        if let Some(cache) = self.cache() {
217            ret.inspect(|uncles| {
218                cache.block_uncles.lock().put(hash.clone(), uncles.clone());
219            })
220        } else {
221            ret
222        }
223    }
224
225    /// Get block extension by block header hash
226    fn get_block_extension(&self, hash: &packed::Byte32) -> Option<packed::Bytes> {
227        if let Some(cache) = self.cache()
228            && let Some(data) = cache.block_extensions.lock().get(hash)
229        {
230            return data.clone();
231        };
232
233        let ret = self
234            .get(COLUMN_BLOCK_EXTENSION, hash.as_slice())
235            .map(|slice| packed::BytesReader::from_slice_should_be_ok(slice.as_ref()).to_entity());
236
237        if let Some(cache) = self.cache() {
238            cache.block_extensions.lock().put(hash.clone(), ret.clone());
239        }
240        ret
241    }
242
243    /// Get block ext by block header hash
244    ///
245    /// Since v0.106, `BlockExt` added two option fields, so we have to use compatibility mode to read
246    fn get_block_ext(&self, block_hash: &packed::Byte32) -> Option<BlockExt> {
247        self.get(COLUMN_BLOCK_EXT, block_hash.as_slice())
248            .map(|slice| {
249                let reader =
250                    packed::BlockExtReader::from_compatible_slice_should_be_ok(slice.as_ref());
251                match reader.count_extra_fields() {
252                    0 => reader.into(),
253                    2 => packed::BlockExtV1Reader::from_slice_should_be_ok(slice.as_ref()).into(),
254                    _ => {
255                        panic!(
256                            "BlockExt storage field count doesn't match, expect 7 or 5, actual {}",
257                            reader.field_count()
258                        )
259                    }
260                }
261            })
262    }
263
264    /// Get block header hash by block number
265    fn get_block_hash(&self, number: BlockNumber) -> Option<packed::Byte32> {
266        let block_number: packed::Uint64 = number.into();
267        self.get(COLUMN_INDEX, block_number.as_slice())
268            .map(|raw| packed::Byte32Reader::from_slice_should_be_ok(raw.as_ref()).to_entity())
269    }
270
271    /// Get block number by block header hash
272    fn get_block_number(&self, hash: &packed::Byte32) -> Option<BlockNumber> {
273        self.get(COLUMN_INDEX, hash.as_slice())
274            .map(|raw| packed::Uint64Reader::from_slice_should_be_ok(raw.as_ref()).into())
275    }
276
277    /// Returns true if the block is on the main chain.
278    fn is_main_chain(&self, hash: &packed::Byte32) -> bool {
279        self.get(COLUMN_INDEX, hash.as_slice()).is_some()
280    }
281
282    /// Returns the header of the chain tip.
283    fn get_tip_header(&self) -> Option<HeaderView> {
284        self.get(COLUMN_META, META_TIP_HEADER_KEY).and_then(|raw| {
285            self.get_block_header(
286                &packed::Byte32Reader::from_slice_should_be_ok(raw.as_ref()).to_entity(),
287            )
288        })
289    }
290
291    /// Returns true if the transaction confirmed in main chain.
292    ///
293    /// This function is base on transaction index `COLUMN_TRANSACTION_INFO`.
294    /// Current release maintains a full index of historical transaction by default, this may be changed in future
295    fn transaction_exists(&self, hash: &packed::Byte32) -> bool {
296        self.get(COLUMN_TRANSACTION_INFO, hash.as_slice()).is_some()
297    }
298
299    /// Get commit transaction and block hash by its hash
300    fn get_transaction(&self, hash: &packed::Byte32) -> Option<(TransactionView, packed::Byte32)> {
301        self.get_transaction_with_info(hash)
302            .map(|(tx, tx_info)| (tx, tx_info.block_hash))
303    }
304
305    /// Returns transaction info by transaction hash.
306    fn get_transaction_info(&self, hash: &packed::Byte32) -> Option<TransactionInfo> {
307        self.get(COLUMN_TRANSACTION_INFO, hash.as_slice())
308            .map(|slice| {
309                let reader = packed::TransactionInfoReader::from_slice_should_be_ok(slice.as_ref());
310                Into::<TransactionInfo>::into(reader)
311            })
312    }
313
314    /// Gets transaction and associated info with correspond hash
315    fn get_transaction_with_info(
316        &self,
317        hash: &packed::Byte32,
318    ) -> Option<(TransactionView, TransactionInfo)> {
319        let tx_info = self.get_transaction_info(hash)?;
320        if let Some(freezer) = self.freezer()
321            && tx_info.block_number > 0
322            && tx_info.block_number < freezer.number()
323        {
324            let raw_block = freezer
325                .retrieve(tx_info.block_number)
326                .expect("block frozen")?;
327            let raw_block_reader =
328                packed::BlockReader::from_compatible_slice(&raw_block).expect("checked data");
329            let tx_reader = raw_block_reader.transactions().get(tx_info.index)?;
330            return Some((tx_reader.to_entity().into_view(), tx_info));
331        }
332        self.get(COLUMN_BLOCK_BODY, tx_info.key().as_slice())
333            .map(|slice| {
334                let reader = packed::TransactionViewReader::from_slice_should_be_ok(slice.as_ref());
335                (reader.into(), tx_info)
336            })
337    }
338
339    /// Return whether cell is live
340    fn have_cell(&self, out_point: &OutPoint) -> bool {
341        let key = out_point.to_cell_key();
342        self.get(COLUMN_CELL, &key).is_some()
343    }
344
345    /// Gets cell meta data with out_point
346    fn get_cell(&self, out_point: &OutPoint) -> Option<CellMeta> {
347        let key = out_point.to_cell_key();
348        self.get(COLUMN_CELL, &key).map(|slice| {
349            let reader = packed::CellEntryReader::from_slice_should_be_ok(slice.as_ref());
350            build_cell_meta_from_reader(out_point.clone(), reader)
351        })
352    }
353
354    /// Returns cell data and its hash for the given outpoint.
355    fn get_cell_data(&self, out_point: &OutPoint) -> Option<(Bytes, packed::Byte32)> {
356        let key = out_point.to_cell_key();
357        if let Some(cache) = self.cache()
358            && let Some(cached) = cache.cell_data.lock().get(&key)
359        {
360            return Some(cached.clone());
361        };
362
363        let ret = self.get(COLUMN_CELL_DATA, &key).map(|slice| {
364            if !slice.as_ref().is_empty() {
365                let reader = packed::CellDataEntryReader::from_slice_should_be_ok(slice.as_ref());
366                let data = reader.output_data().into();
367                let data_hash = reader.output_data_hash().to_entity();
368                (data, data_hash)
369            } else {
370                (Bytes::new(), packed::Byte32::zero())
371            }
372        });
373
374        if let Some(cache) = self.cache() {
375            ret.inspect(|cached| {
376                cache.cell_data.lock().put(key, cached.clone());
377            })
378        } else {
379            ret
380        }
381    }
382
383    /// Returns the hash of cell data for the given outpoint.
384    fn get_cell_data_hash(&self, out_point: &OutPoint) -> Option<packed::Byte32> {
385        let key = out_point.to_cell_key();
386        if let Some(cache) = self.cache()
387            && let Some(cached) = cache.cell_data_hash.lock().get(&key)
388        {
389            return Some(cached.clone());
390        };
391
392        let ret = self.get(COLUMN_CELL_DATA_HASH, &key).map(|raw| {
393            if !raw.as_ref().is_empty() {
394                packed::Byte32Reader::from_slice_should_be_ok(raw.as_ref()).to_entity()
395            } else {
396                packed::Byte32::zero()
397            }
398        });
399
400        if let Some(cache) = self.cache() {
401            ret.inspect(|cached| {
402                cache.cell_data_hash.lock().put(key, cached.clone());
403            })
404        } else {
405            ret
406        }
407    }
408
409    /// Gets current epoch ext
410    fn get_current_epoch_ext(&self) -> Option<EpochExt> {
411        self.get(COLUMN_META, META_CURRENT_EPOCH_KEY)
412            .map(|slice| packed::EpochExtReader::from_slice_should_be_ok(slice.as_ref()).into())
413    }
414
415    /// Gets epoch ext by epoch index
416    fn get_epoch_ext(&self, hash: &packed::Byte32) -> Option<EpochExt> {
417        self.get(COLUMN_EPOCH, hash.as_slice())
418            .map(|slice| packed::EpochExtReader::from_slice_should_be_ok(slice.as_ref()).into())
419    }
420
421    /// Gets epoch index by epoch number
422    fn get_epoch_index(&self, number: EpochNumber) -> Option<packed::Byte32> {
423        let epoch_number: packed::Uint64 = number.into();
424        self.get(COLUMN_EPOCH, epoch_number.as_slice())
425            .map(|raw| packed::Byte32Reader::from_slice_should_be_ok(raw.as_ref()).to_entity())
426    }
427
428    /// Gets epoch index by block hash
429    fn get_block_epoch_index(&self, block_hash: &packed::Byte32) -> Option<packed::Byte32> {
430        self.get(COLUMN_BLOCK_EPOCH, block_hash.as_slice())
431            .map(|raw| packed::Byte32Reader::from_slice_should_be_ok(raw.as_ref()).to_entity())
432    }
433
434    /// Returns the epoch of the block with the given hash.
435    fn get_block_epoch(&self, hash: &packed::Byte32) -> Option<EpochExt> {
436        self.get_block_epoch_index(hash)
437            .and_then(|index| self.get_epoch_ext(&index))
438    }
439
440    /// Returns true if the given hash is an uncle block.
441    fn is_uncle(&self, hash: &packed::Byte32) -> bool {
442        self.get(COLUMN_UNCLES, hash.as_slice()).is_some()
443    }
444
445    /// Gets header by uncle header hash
446    fn get_uncle_header(&self, hash: &packed::Byte32) -> Option<HeaderView> {
447        self.get(COLUMN_UNCLES, hash.as_slice()).map(|slice| {
448            let reader = packed::HeaderViewReader::from_slice_should_be_ok(slice.as_ref());
449            Into::<HeaderView>::into(reader)
450        })
451    }
452
453    /// Returns true if a block with the given hash exists in the store.
454    fn block_exists(&self, hash: &packed::Byte32) -> bool {
455        if let Some(cache) = self.cache()
456            && cache.headers.lock().get(hash).is_some()
457        {
458            return true;
459        };
460        self.get(COLUMN_BLOCK_HEADER, hash.as_slice()).is_some()
461    }
462
463    /// Gets cellbase by block hash
464    fn get_cellbase(&self, hash: &packed::Byte32) -> Option<TransactionView> {
465        let key = packed::TransactionKey::new_builder()
466            .block_hash(hash.to_owned())
467            .build();
468        self.get(COLUMN_BLOCK_BODY, key.as_slice()).map(|slice| {
469            let reader = packed::TransactionViewReader::from_slice_should_be_ok(slice.as_ref());
470            Into::<TransactionView>::into(reader)
471        })
472    }
473
474    /// Gets latest built filter data block hash
475    fn get_latest_built_filter_data_block_hash(&self) -> Option<packed::Byte32> {
476        self.get(COLUMN_META, META_LATEST_BUILT_FILTER_DATA_KEY)
477            .map(|raw| packed::Byte32Reader::from_slice_should_be_ok(raw.as_ref()).to_entity())
478    }
479
480    /// Gets block filter data by block hash
481    fn get_block_filter(&self, hash: &packed::Byte32) -> Option<packed::Bytes> {
482        self.get(COLUMN_BLOCK_FILTER, hash.as_slice())
483            .map(|slice| packed::BytesReader::from_slice_should_be_ok(slice.as_ref()).to_entity())
484    }
485
486    /// Gets block filter hash by block hash
487    fn get_block_filter_hash(&self, hash: &packed::Byte32) -> Option<packed::Byte32> {
488        self.get(COLUMN_BLOCK_FILTER_HASH, hash.as_slice())
489            .map(|slice| packed::Byte32Reader::from_slice_should_be_ok(slice.as_ref()).to_entity())
490    }
491
492    /// Gets block bytes by block hash
493    fn get_packed_block(&self, hash: &packed::Byte32) -> Option<packed::Block> {
494        let header = self
495            .get(COLUMN_BLOCK_HEADER, hash.as_slice())
496            .map(|slice| {
497                let reader = packed::HeaderViewReader::from_slice_should_be_ok(slice.as_ref());
498                reader.data().to_entity()
499            })?;
500
501        let prefix = hash.as_slice();
502        let transactions: packed::TransactionVec = self
503            .get_iter(
504                COLUMN_BLOCK_BODY,
505                IteratorMode::From(prefix, Direction::Forward),
506            )
507            .take_while(|(key, _)| key.starts_with(prefix))
508            .map(|(_key, value)| {
509                let reader = packed::TransactionViewReader::from_slice_should_be_ok(value.as_ref());
510                reader.data().to_entity()
511            })
512            .collect::<Vec<_>>()
513            .into();
514
515        let uncles = self.get_block_uncles(hash)?;
516        let proposals = self.get_block_proposal_txs_ids(hash)?;
517        let extension_opt = self.get_block_extension(hash);
518
519        let block = if let Some(extension) = extension_opt {
520            packed::BlockV1::new_builder()
521                .header(header)
522                .uncles(uncles.data())
523                .transactions(transactions)
524                .proposals(proposals)
525                .extension(extension)
526                .build()
527                .as_v0()
528        } else {
529            packed::Block::new_builder()
530                .header(header)
531                .uncles(uncles.data())
532                .transactions(transactions)
533                .proposals(proposals)
534                .build()
535        };
536
537        Some(block)
538    }
539
540    /// Gets block header bytes by block hash
541    fn get_packed_block_header(&self, hash: &packed::Byte32) -> Option<packed::Header> {
542        self.get(COLUMN_BLOCK_HEADER, hash.as_slice()).map(|slice| {
543            let reader = packed::HeaderViewReader::from_slice_should_be_ok(slice.as_ref());
544            reader.data().to_entity()
545        })
546    }
547
548    /// Gets a header digest.
549    fn get_header_digest(&self, position_u64: u64) -> Option<packed::HeaderDigest> {
550        let position: packed::Uint64 = position_u64.into();
551        self.get(COLUMN_CHAIN_ROOT_MMR, position.as_slice())
552            .map(|slice| {
553                let reader = packed::HeaderDigestReader::from_slice_should_be_ok(slice.as_ref());
554                reader.to_entity()
555            })
556    }
557
558    /// Gets ancestor block header by a base block hash and number
559    fn get_ancestor(&self, base: &packed::Byte32, number: BlockNumber) -> Option<HeaderView> {
560        let header = self.get_block_header(base)?;
561        if number > header.number() {
562            None
563        } else if number == header.number() {
564            Some(header)
565        } else if self.is_main_chain(base) {
566            self.get_block_hash(number)
567                .and_then(|hash| self.get_block_header(&hash))
568        } else {
569            self.get_ancestor(&header.parent_hash(), number)
570        }
571    }
572}
573
574fn build_cell_meta_from_reader(out_point: OutPoint, reader: packed::CellEntryReader) -> CellMeta {
575    CellMeta {
576        out_point,
577        cell_output: reader.output().to_entity(),
578        transaction_info: Some(TransactionInfo {
579            block_number: reader.block_number().into(),
580            block_hash: reader.block_hash().to_entity(),
581            block_epoch: reader.block_epoch().into(),
582            index: reader.index().into(),
583        }),
584        data_bytes: reader.data_size().into(),
585        mem_cell_data: None,
586        mem_cell_data_hash: None,
587    }
588}