ckb_store/
cell.rs

1use crate::{ChainStore, StoreTransaction};
2use ckb_error::Error;
3use ckb_types::{core::BlockView, packed, prelude::*};
4use std::collections::HashMap;
5
6/*
7 * Live cell entry.
8 *
9 *  table CellEntry {
10 *      output:                CellOutput,
11 *      block_hash:            Byte32,
12 *      block_number:          Uint64,
13 *      block_epoch:           Uint64,
14 *      index:                 Uint32,
15 *      data_size:             Uint64,
16 *  }
17 *
18 *
19 *  table CellDataEntry {
20 *      output_data:           Bytes,
21 *      output_data_hash:      Byte32,
22 *  }
23 */
24
25/// Apply the effects of this block on the live cell set.
26pub fn attach_block_cell(txn: &StoreTransaction, block: &BlockView) -> Result<(), Error> {
27    let transactions = block.transactions();
28
29    // add new live cells
30    let new_cells = transactions
31        .iter()
32        .enumerate()
33        .flat_map(move |(tx_index, tx)| {
34            let tx_hash = tx.hash();
35            let block_hash = block.header().hash();
36            let block_number = block.header().number();
37            let block_epoch = block.header().epoch();
38
39            tx.outputs_with_data_iter()
40                .enumerate()
41                .map(move |(index, (cell_output, data))| {
42                    let out_point = packed::OutPoint::new_builder()
43                        .tx_hash(tx_hash.clone())
44                        .index(index)
45                        .build();
46
47                    let entry = packed::CellEntryBuilder::default()
48                        .output(cell_output)
49                        .block_hash(block_hash.clone())
50                        .block_number(block_number)
51                        .block_epoch(block_epoch)
52                        .index(tx_index)
53                        .data_size(data.len() as u64)
54                        .build();
55
56                    let data_entry = if !data.is_empty() {
57                        let data_hash = packed::CellOutput::calc_data_hash(&data);
58                        Some(
59                            packed::CellDataEntryBuilder::default()
60                                .output_data(data)
61                                .output_data_hash(data_hash)
62                                .build(),
63                        )
64                    } else {
65                        None
66                    };
67
68                    (out_point, entry, data_entry)
69                })
70        });
71    txn.insert_cells(new_cells)?;
72
73    // mark inputs dead
74    // skip cellbase
75    let deads = transactions
76        .iter()
77        .skip(1)
78        .flat_map(|tx| tx.input_pts_iter());
79    txn.delete_cells(deads)?;
80
81    Ok(())
82}
83
84/// Undoes the effects of this block on the live cell set.
85pub fn detach_block_cell(txn: &StoreTransaction, block: &BlockView) -> Result<(), Error> {
86    let transactions = block.transactions();
87    let mut input_pts = HashMap::with_capacity(transactions.len());
88
89    for tx in transactions.iter().skip(1) {
90        for pts in tx.input_pts_iter() {
91            let tx_hash = pts.tx_hash();
92            let index: usize = pts.index().into();
93            let indexes = input_pts.entry(tx_hash).or_insert_with(Vec::new);
94            indexes.push(index);
95        }
96    }
97
98    // restore inputs
99    // skip cellbase
100    let undo_deads = input_pts
101        .iter()
102        .filter_map(|(tx_hash, indexes)| {
103            txn.get_transaction_with_info(tx_hash)
104                .map(move |(tx, info)| {
105                    let block_hash = info.block_hash;
106                    let block_number = info.block_number;
107                    let block_epoch = info.block_epoch;
108                    let tx_index = info.index;
109
110                    indexes.iter().filter_map(move |index| {
111                        tx.output_with_data(*index).map(|(cell_output, data)| {
112                            let out_point = packed::OutPoint::new_builder()
113                                .tx_hash(tx_hash.clone())
114                                .index(index)
115                                .build();
116
117                            let entry = packed::CellEntryBuilder::default()
118                                .output(cell_output)
119                                .block_hash(block_hash.clone())
120                                .block_number(block_number)
121                                .block_epoch(block_epoch)
122                                .index(tx_index)
123                                .data_size(data.len() as u64)
124                                .build();
125
126                            let data_entry = if !data.is_empty() {
127                                let data_hash = packed::CellOutput::calc_data_hash(&data);
128                                Some(
129                                    packed::CellDataEntryBuilder::default()
130                                        .output_data(data)
131                                        .output_data_hash(data_hash)
132                                        .build(),
133                                )
134                            } else {
135                                None
136                            };
137
138                            (out_point, entry, data_entry)
139                        })
140                    })
141                })
142        })
143        .flatten();
144    txn.insert_cells(undo_deads)?;
145
146    // undo live cells
147    let undo_cells = transactions.iter().flat_map(|tx| tx.output_pts_iter());
148    txn.delete_cells(undo_cells)?;
149
150    Ok(())
151}