ckb_types/
extension.rs

1use std::collections::HashSet;
2
3use crate::{
4    core::{self},
5    packed,
6    prelude::*,
7    utilities::{compact_to_difficulty, merkle_root},
8    U256,
9};
10
11impl Difficulty for packed::RawHeader {
12    /// Calculates the difficulty from compact target.
13    fn difficulty(&self) -> U256 {
14        compact_to_difficulty(self.compact_target().unpack())
15    }
16}
17
18impl Difficulty for packed::Header {
19    /// Calculates the difficulty from compact target.
20    fn difficulty(&self) -> U256 {
21        self.raw().difficulty()
22    }
23}
24
25impl ResetBlock for packed::Block {
26    /// Recalculates all hashes and merkle roots in the header.
27    fn reset_header(self) -> packed::Block {
28        let tx_hashes = self.as_reader().calc_tx_hashes();
29        let tx_witness_hashes = self.as_reader().calc_tx_witness_hashes();
30        self.reset_header_with_hashes(&tx_hashes[..], &tx_witness_hashes[..])
31    }
32
33    fn reset_header_with_hashes(
34        self,
35        tx_hashes: &[packed::Byte32],
36        tx_witness_hashes: &[packed::Byte32],
37    ) -> packed::Block {
38        let raw_transactions_root = merkle_root(tx_hashes);
39        let witnesses_root = merkle_root(tx_witness_hashes);
40        let transactions_root = merkle_root(&[raw_transactions_root, witnesses_root]);
41        let proposals_hash = self.as_reader().calc_proposals_hash();
42        let extra_hash = self.as_reader().calc_extra_hash().extra_hash();
43        let raw_header = self
44            .header()
45            .raw()
46            .as_builder()
47            .transactions_root(transactions_root)
48            .proposals_hash(proposals_hash)
49            .extra_hash(extra_hash)
50            .build();
51        let header = self.header().as_builder().raw(raw_header).build();
52        if let Some(extension) = self.extension() {
53            packed::BlockV1::new_builder()
54                .header(header)
55                .uncles(self.uncles())
56                .transactions(self.transactions())
57                .proposals(self.proposals())
58                .extension(extension)
59                .build()
60                .as_v0()
61        } else {
62            self.as_builder().header(header).build()
63        }
64    }
65}
66
67impl BuildCompactBlock for packed::CompactBlock {
68    /// Builds a `CompactBlock` from block and prefilled transactions indexes.
69    fn build_from_block(
70        block: &core::BlockView,
71        prefilled_transactions_indexes: &HashSet<usize>,
72    ) -> packed::CompactBlock {
73        // always prefill cellbase
74        let prefilled_transactions_len = prefilled_transactions_indexes.len() + 1;
75        let mut short_ids: Vec<packed::ProposalShortId> = Vec::with_capacity(
76            block
77                .data()
78                .transactions()
79                .len()
80                .saturating_sub(prefilled_transactions_len),
81        );
82        let mut prefilled_transactions = Vec::with_capacity(prefilled_transactions_len);
83
84        for (transaction_index, transaction) in block.transactions().into_iter().enumerate() {
85            if prefilled_transactions_indexes.contains(&transaction_index)
86                || transaction.is_cellbase()
87            {
88                let prefilled_tx = packed::IndexTransaction::new_builder()
89                    .index((transaction_index as u32).pack())
90                    .transaction(transaction.data())
91                    .build();
92                prefilled_transactions.push(prefilled_tx);
93            } else {
94                short_ids.push(transaction.proposal_short_id());
95            }
96        }
97
98        if let Some(extension) = block.data().extension() {
99            packed::CompactBlockV1::new_builder()
100                .header(block.data().header())
101                .short_ids(short_ids.pack())
102                .prefilled_transactions(prefilled_transactions.pack())
103                .uncles(block.uncle_hashes.clone())
104                .proposals(block.data().proposals())
105                .extension(extension)
106                .build()
107                .as_v0()
108        } else {
109            packed::CompactBlock::new_builder()
110                .header(block.data().header())
111                .short_ids(short_ids.pack())
112                .prefilled_transactions(prefilled_transactions.pack())
113                .uncles(block.uncle_hashes.clone())
114                .proposals(block.data().proposals())
115                .build()
116        }
117    }
118
119    /// Takes proposal short ids for the transactions which are not prefilled.
120    fn block_short_ids(&self) -> Vec<Option<packed::ProposalShortId>> {
121        let txs_len = self.txs_len();
122        let mut block_short_ids: Vec<Option<packed::ProposalShortId>> = Vec::with_capacity(txs_len);
123        let prefilled_indexes = self
124            .prefilled_transactions()
125            .into_iter()
126            .map(|tx_index| tx_index.index().unpack())
127            .collect::<HashSet<usize>>();
128
129        let mut index = 0;
130        for i in 0..txs_len {
131            if prefilled_indexes.contains(&i) {
132                block_short_ids.push(None);
133            } else {
134                block_short_ids.push(self.short_ids().get(index));
135                index += 1;
136            }
137        }
138        block_short_ids
139    }
140
141    /// Collects the short id indexes.
142    fn short_id_indexes(&self) -> Vec<usize> {
143        let prefilled_indexes_iter = self
144            .prefilled_transactions()
145            .into_iter()
146            .map(|i| i.index().unpack());
147
148        let prefilled_indexes: HashSet<usize> = prefilled_indexes_iter.collect();
149
150        (0..self.txs_len())
151            .filter(|index| !prefilled_indexes.contains(index))
152            .collect()
153    }
154}
155
156impl<'r> CalcExtraHash for packed::BlockReader<'r> {
157    /// Calculates the extra hash, which is a combination of the uncles hash and
158    /// the extension hash.
159    ///
160    /// - If there is no extension, extra hash is the same as the uncles hash.
161    /// - If there is a extension, then extra hash it the hash of the combination
162    ///   of uncles hash and the extension hash.
163    fn calc_extra_hash(&self) -> core::ExtraHashView {
164        crate::core::ExtraHashView::new(self.calc_uncles_hash(), self.calc_extension_hash())
165    }
166}
167
168impl CalcExtraHash for packed::Block {
169    /// Calls [`BlockReader.calc_extra_hash()`](struct.BlockReader.html#method.calc_extra_hash)
170    /// for [`self.as_reader()`](struct.Block.html#method.as_reader).
171    fn calc_extra_hash(&self) -> core::ExtraHashView {
172        self.as_reader().calc_extra_hash()
173    }
174}
175
176#[cfg(test)]
177mod test {
178    use crate::{h256, packed, prelude::*};
179    #[test]
180    fn empty_extra_hash() {
181        let block = packed::Block::new_builder().build();
182        let expect = h256!("0x0");
183        assert_eq!(block.calc_extra_hash().extra_hash(), expect.pack());
184    }
185}