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