cfx_rpc/helpers/
fee_history_cache.rs1#![allow(dead_code)]
2use cfx_rpc_cfx_types::{FeeHistoryCacheEntry, PhantomBlock};
3use cfx_types::{Space, H256};
4use parking_lot::RwLock;
5use std::{collections::VecDeque, sync::Arc};
6
7pub const MAX_FEE_HISTORY_CACHE_BLOCK_COUNT: u64 = 1024;
8
9#[derive(Debug, Clone)]
10pub struct FeeHistoryCache {
11 inner: Arc<RwLock<FeeHistoryCacheInner>>,
12}
13
14impl FeeHistoryCache {
15 pub fn new() -> Self {
16 Self {
17 inner: Arc::new(RwLock::new(FeeHistoryCacheInner::new())),
18 }
19 }
20
21 pub fn update_to_latest_block<F>(
31 &self, latest_block: u64, latest_hash: H256, query_len: u64,
32 fetch_block_by_hash: F,
33 ) -> Result<(), String>
34 where
35 F: Fn(H256) -> Result<PhantomBlock, String>,
36 {
37 let mut inner = self.inner.write();
38
39 inner.check_and_clear_cache(latest_block);
41
42 let start_block = if inner.is_empty() {
43 if latest_block <= query_len {
44 0
45 } else {
46 latest_block - query_len + 1
47 }
48 } else {
49 inner.upper_bound() + 1
50 };
51
52 let mut curr_hash = latest_hash;
53 let mut container = VecDeque::new();
54 for i in (start_block..=latest_block).rev() {
55 let block = fetch_block_by_hash(curr_hash)?;
56 container.push_front((
57 i,
58 FeeHistoryCacheEntry::from_block(
59 Space::Ethereum,
60 &block.pivot_header,
61 block.transactions.iter().map(|x| &**x),
62 ),
63 ));
64 curr_hash = block.pivot_header.parent_hash().clone();
65 }
66
67 for (block_number, entry) in container {
68 inner.push_back(block_number, entry)?;
69 }
70
71 if inner.lower_bound < start_block {
73 let need_check = start_block - inner.lower_bound;
74 for item in inner.entries.iter_mut().take(need_check as usize).rev()
75 {
76 if item.header_hash == curr_hash {
77 break;
78 }
79 let block = fetch_block_by_hash(curr_hash)?;
80 *item = FeeHistoryCacheEntry::from_block(
81 Space::Ethereum,
82 &block.pivot_header,
83 block.transactions.iter().map(|x| &**x),
84 );
85 curr_hash = block.pivot_header.parent_hash().clone();
86 }
87 }
88
89 Ok(())
90 }
91
92 pub fn max_blocks(&self) -> u64 { self.inner.read().max_blocks }
93
94 pub fn lower_bound(&self) -> u64 { self.inner.read().lower_bound }
95
96 pub fn upper_bound(&self) -> u64 { self.inner.read().upper_bound() }
97
98 pub fn get_history(
99 &self, start_block: u64, end_block: u64,
100 ) -> Option<Vec<FeeHistoryCacheEntry>> {
101 let inner = self.inner.read();
102 let lower_bound = inner.lower_bound;
103 let upper_bound = inner.upper_bound();
104 if start_block >= lower_bound && end_block <= upper_bound {
105 let start = (start_block - lower_bound) as usize;
106 let end = (end_block - lower_bound) as usize;
107 let result = inner
108 .entries
109 .range(start..=end)
110 .map(|fee_entry| fee_entry.clone())
111 .collect::<Vec<_>>();
112
113 if result.is_empty() {
114 return None;
115 }
116
117 Some(result)
118 } else {
119 None
120 }
121 }
122
123 pub fn get_history_with_missing_info(
124 &self, start_block: u64, end_block: u64,
125 ) -> Vec<Option<FeeHistoryCacheEntry>> {
126 let inner = self.inner.read();
127 (start_block..=end_block)
128 .map(|block_number| inner.get(block_number))
129 .collect()
130 }
131}
132
133#[derive(Debug)]
134struct FeeHistoryCacheInner {
135 lower_bound: u64,
137 max_blocks: u64,
139 entries: VecDeque<FeeHistoryCacheEntry>,
141}
142
143impl FeeHistoryCacheInner {
144 pub fn new() -> Self {
145 Self {
146 lower_bound: 0,
147 max_blocks: MAX_FEE_HISTORY_CACHE_BLOCK_COUNT,
148 entries: VecDeque::new(),
149 }
150 }
151
152 pub fn upper_bound(&self) -> u64 {
153 self.lower_bound + self.entries.len() as u64 - 1
154 }
155
156 fn check_and_clear_cache(&mut self, latest_block: u64) {
158 if !self.is_empty()
159 && self.upper_bound() + self.max_blocks <= latest_block
160 {
161 self.clear_cache();
162 }
163 }
164
165 pub fn clear_cache(&mut self) {
166 self.entries.clear();
167 self.lower_bound = 0;
168 }
169
170 fn push_back(
171 &mut self, block_number: u64, entry: FeeHistoryCacheEntry,
172 ) -> Result<(), String> {
173 if !self.is_empty() && block_number - self.upper_bound() != 1 {
174 return Err("block number is not consecutive".to_string());
175 }
176
177 self.entries.push_back(entry);
178
179 if self.lower_bound == 0 {
181 self.lower_bound = block_number;
182 }
183
184 if self.entries.len() > self.max_blocks as usize {
185 self.entries.pop_front();
186 self.lower_bound += 1;
187 }
188
189 Ok(())
190 }
191
192 pub fn is_empty(&self) -> bool { self.entries.is_empty() }
193
194 pub fn get(&self, height: u64) -> Option<FeeHistoryCacheEntry> {
195 if height < self.lower_bound || height > self.upper_bound() {
196 return None;
197 }
198 let key = height - self.lower_bound;
199 self.entries.get(key as usize).map(|item| item.clone())
200 }
201
202 #[allow(dead_code)]
203 pub fn update(&mut self, height: u64, entry: FeeHistoryCacheEntry) {
204 if height < self.lower_bound || height > self.upper_bound() {
205 return;
206 }
207 let key = height - self.lower_bound;
208 if let Some(item) = self.entries.get_mut(key as usize) {
209 *item = entry;
210 }
211 }
212}