ckb_proposal_table/
lib.rs1use ckb_chain_spec::consensus::ProposalWindow;
4use ckb_types::{core::BlockNumber, packed::ProposalShortId};
5use std::collections::{BTreeMap, HashSet};
6use std::ops::Bound;
7
8#[cfg(test)]
9mod tests;
10
11#[derive(Default, Clone, Debug)]
15pub struct ProposalView {
16 pub(crate) gap: HashSet<ProposalShortId>,
17 pub(crate) set: HashSet<ProposalShortId>,
18}
19
20impl ProposalView {
21 pub fn new(gap: HashSet<ProposalShortId>, set: HashSet<ProposalShortId>) -> ProposalView {
23 ProposalView { gap, set }
24 }
25
26 pub fn gap(&self) -> &HashSet<ProposalShortId> {
28 &self.gap
29 }
30
31 pub fn set(&self) -> &HashSet<ProposalShortId> {
33 &self.set
34 }
35
36 pub fn contains_proposed(&self, id: &ProposalShortId) -> bool {
38 self.set.contains(id)
39 }
40
41 pub fn contains_gap(&self, id: &ProposalShortId) -> bool {
43 self.gap.contains(id)
44 }
45}
46
47#[derive(Debug, PartialEq, Clone, Eq)]
49pub struct ProposalTable {
50 pub(crate) table: BTreeMap<BlockNumber, HashSet<ProposalShortId>>,
51 pub(crate) proposal_window: ProposalWindow,
52}
53
54impl ProposalTable {
55 pub fn new(proposal_window: ProposalWindow) -> Self {
57 ProposalTable {
58 proposal_window,
59 table: BTreeMap::default(),
60 }
61 }
62
63 pub fn insert(&mut self, number: BlockNumber, ids: HashSet<ProposalShortId>) -> bool {
67 self.table.insert(number, ids).is_none()
68 }
69
70 pub fn remove(&mut self, number: BlockNumber) -> Option<HashSet<ProposalShortId>> {
83 self.table.remove(&number)
84 }
85
86 pub fn all(&self) -> &BTreeMap<BlockNumber, HashSet<ProposalShortId>> {
88 &self.table
89 }
90
91 pub fn finalize(
94 &mut self,
95 origin: &ProposalView,
96 number: BlockNumber,
97 ) -> (HashSet<ProposalShortId>, ProposalView) {
98 let candidate_number = number + 1;
99 let proposal_start = candidate_number.saturating_sub(self.proposal_window.farthest());
100 let proposal_end = candidate_number.saturating_sub(self.proposal_window.closest());
101
102 if proposal_start > 1 {
103 self.table = self.table.split_off(&proposal_start);
104 }
105
106 ckb_logger::trace!("[proposal_finalize] table {:?}", self.table);
107
108 let (new_ids, gap) = if candidate_number <= self.proposal_window.closest() {
116 (
117 HashSet::new(),
118 self.table
119 .range((Bound::Unbounded, Bound::Included(&number)))
120 .flat_map(|pair| pair.1)
121 .cloned()
122 .collect(),
123 )
124 } else {
125 (
126 self.table
127 .range((
128 Bound::Included(&proposal_start),
129 Bound::Included(&proposal_end),
130 ))
131 .flat_map(|pair| pair.1)
132 .cloned()
133 .collect(),
134 self.table
135 .range((Bound::Excluded(&proposal_end), Bound::Included(&number)))
136 .flat_map(|pair| pair.1)
137 .cloned()
138 .collect(),
139 )
140 };
141
142 let removed_ids: HashSet<ProposalShortId> =
143 origin.set().difference(&new_ids).cloned().collect();
144 ckb_logger::trace!(
145 "[proposal_finalize] number {} proposal_start {}----proposal_end {}",
146 number,
147 proposal_start,
148 proposal_end
149 );
150 ckb_logger::trace!(
151 "[proposal_finalize] number {} new_ids {:?}----removed_ids {:?}",
152 number,
153 new_ids,
154 removed_ids
155 );
156 (removed_ids, ProposalView::new(gap, new_ids))
157 }
158}