1use crate::{ChainStore, StoreTransaction};
2use ckb_error::Error;
3use ckb_types::{core::BlockView, packed, prelude::*};
4use std::collections::HashMap;
5
6pub fn attach_block_cell(txn: &StoreTransaction, block: &BlockView) -> Result<(), Error> {
27 let transactions = block.transactions();
28
29 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 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
84pub 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 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 let undo_cells = transactions.iter().flat_map(|tx| tx.output_pts_iter());
148 txn.delete_cells(undo_cells)?;
149
150 Ok(())
151}