1use std::sync::Arc;
6
7use anyhow::{Context, Result, bail};
8use async_trait::async_trait;
9use committable::Commitment;
10use espresso_types::{
11 Certificate2, ChainConfig, FeeAccount, FeeMerkleTree, Leaf2, NodeState, SeqTypes,
12 retain_accounts,
13 traits::SequencerPersistence,
14 v0_3::{RewardAccountV1, RewardMerkleTreeV1},
15 v0_4::{RewardAccountV2, RewardMerkleTreeV2},
16};
17use hotshot_query_service::{
18 data_source::{
19 VersionedDataSource,
20 storage::{FileSystemStorage, NodeStorage},
21 },
22 node::BlockId,
23};
24use hotshot_types::{
25 data::{VidShare, ViewNumber},
26 vote::HasViewNumber,
27};
28use itertools::Itertools;
29use jf_merkle_tree_compat::{
30 ForgetableMerkleTreeScheme, ForgetableUniversalMerkleTreeScheme, LookupResult,
31 MerkleTreeScheme, UniversalMerkleTreeScheme,
32};
33use request_response::data_source::DataSource as DataSourceTrait;
34
35use super::request::{Request, Response};
36use crate::{
37 api::{BlocksFrontier, RewardMerkleTreeV2Data},
38 catchup::{
39 add_fee_accounts_to_state, add_v1_reward_accounts_to_state, add_v2_reward_accounts_to_state,
40 },
41 consensus_handle::ConsensusHandle,
42};
43
44#[derive(Clone)]
46pub enum Storage {
47 Sql(Arc<dyn RequestStorage>),
48 Fs(Arc<FileSystemStorage<SeqTypes>>),
49}
50
51#[async_trait]
53pub trait RequestStorage: Send + Sync {
54 async fn get_accounts(
55 &self,
56 instance: &NodeState,
57 height: u64,
58 view: ViewNumber,
59 accounts: &[FeeAccount],
60 ) -> Result<(FeeMerkleTree, Leaf2)>;
61
62 async fn get_reward_accounts_v1(
63 &self,
64 instance: &NodeState,
65 height: u64,
66 view: ViewNumber,
67 accounts: &[RewardAccountV1],
68 ) -> Result<(RewardMerkleTreeV1, Leaf2)>;
69
70 async fn get_reward_accounts_v2(
71 &self,
72 instance: &NodeState,
73 height: u64,
74 view: ViewNumber,
75 accounts: &[RewardAccountV2],
76 ) -> Result<(RewardMerkleTreeV2, Leaf2)>;
77
78 async fn get_frontier(
79 &self,
80 instance: &NodeState,
81 height: u64,
82 view: ViewNumber,
83 ) -> Result<BlocksFrontier>;
84
85 async fn get_chain_config(&self, commitment: Commitment<ChainConfig>) -> Result<ChainConfig>;
86
87 async fn get_leaf_chain(&self, height: u64) -> Result<Vec<Leaf2>>;
88
89 async fn load_cert2(&self, height: u64) -> Result<Option<Certificate2<SeqTypes>>>;
90
91 async fn load_tree(&self, height: u64) -> Result<Vec<u8>>;
92
93 async fn get_vid_share(&self, block_number: u64) -> Result<VidShare>;
94}
95
96#[derive(Clone)]
97pub struct DataSource<P: SequencerPersistence> {
98 pub consensus_handle: Arc<ConsensusHandle<SeqTypes, Arc<P>>>,
99 pub node_state: NodeState,
101 pub storage: Option<Storage>,
103 pub persistence: Arc<P>,
105}
106
107impl<P: SequencerPersistence> DataSource<P> {
108 fn sql_storage(&self, what: &str) -> Result<&dyn RequestStorage> {
109 match &self.storage {
110 Some(Storage::Sql(storage)) => Ok(&**storage),
111 Some(Storage::Fs(_)) => bail!("fs storage not supported for {what}"),
112 None => bail!("storage was not initialized"),
113 }
114 }
115}
116
117#[async_trait]
119impl<P: SequencerPersistence> DataSourceTrait<Request> for DataSource<P> {
120 async fn derive_response_for(&self, request: &Request) -> Result<Response> {
121 match request {
122 Request::Accounts(height, view, accounts) => {
123 if let Some(state) = self.consensus_handle.state(ViewNumber::new(*view)).await
125 && let Ok(accounts) =
126 retain_accounts(&state.fee_merkle_tree, accounts.iter().copied())
127 {
128 return Ok(Response::Accounts(accounts));
129 }
130
131 let (merkle_tree, leaf) = self
133 .sql_storage("accounts")?
134 .get_accounts(&self.node_state, *height, ViewNumber::new(*view), accounts)
135 .await
136 .with_context(|| "failed to get accounts from sql storage")?;
137
138 if let Err(err) = add_fee_accounts_to_state(
141 &*self.consensus_handle,
142 &ViewNumber::new(*view),
143 accounts,
144 &merkle_tree,
145 leaf,
146 )
147 .await
148 {
149 tracing::warn!(?view, "Cannot update fetched account state: {err:#}");
150 }
151
152 Ok(Response::Accounts(merkle_tree))
153 },
154
155 Request::Leaf(height) => {
156 if let Ok(leaf_chain) =
159 legacy_leaf_chain_from_memory(&*self.consensus_handle, *height).await
160 {
161 return Ok(Response::Leaf(leaf_chain));
162 }
163
164 let leaf_chain = self
165 .sql_storage("leaf")?
166 .get_leaf_chain(*height)
167 .await
168 .with_context(|| "failed to get leaf from sql storage")?;
169
170 Ok(Response::Leaf(leaf_chain))
171 },
172 Request::ChainConfig(commitment) => {
173 if let Some(state) = self.consensus_handle.decided_state().await {
175 let chain_config_from_memory = state.chain_config;
176 if chain_config_from_memory.commit() == *commitment
177 && let Some(chain_config) = chain_config_from_memory.resolve()
178 {
179 return Ok(Response::ChainConfig(chain_config));
180 }
181 }
182
183 Ok(Response::ChainConfig(
185 self.sql_storage("chain config")?
186 .get_chain_config(*commitment)
187 .await
188 .with_context(|| "failed to get chain config from sql storage")?,
189 ))
190 },
191 Request::BlocksFrontier(height, view) => {
192 let blocks_frontier_from_memory: Option<Result<BlocksFrontier>> = self
194 .consensus_handle
195 .state(ViewNumber::new(*view))
196 .await
197 .map(|state| {
198 let tree = &state.block_merkle_tree;
199 let frontier = tree.lookup(tree.num_leaves() - 1).expect_ok()?.1;
200 Ok(frontier)
201 });
202
203 if let Some(Ok(blocks_frontier_from_memory)) = blocks_frontier_from_memory {
204 return Ok(Response::BlocksFrontier(blocks_frontier_from_memory));
205 } else {
206 let blocks_frontier_from_storage = self
208 .sql_storage("blocks frontier")?
209 .get_frontier(&self.node_state, *height, ViewNumber::new(*view))
210 .await
211 .with_context(|| "failed to get blocks frontier from sql storage")?;
212
213 Ok(Response::BlocksFrontier(blocks_frontier_from_storage))
214 }
215 },
216 Request::RewardAccountsV2(height, view, accounts) => {
217 if let Some(state) = self.consensus_handle.state(ViewNumber::new(*view)).await
219 && let Ok(reward_accounts) = retain_v2_reward_accounts(
220 &state.reward_merkle_tree_v2,
221 accounts.iter().copied(),
222 )
223 {
224 return Ok(Response::RewardAccountsV2(reward_accounts));
225 }
226
227 let (merkle_tree, leaf) = self
229 .sql_storage("reward accounts")?
230 .get_reward_accounts_v2(
231 &self.node_state,
232 *height,
233 ViewNumber::new(*view),
234 accounts,
235 )
236 .await
237 .with_context(|| "failed to get accounts from sql storage")?;
238
239 if let Err(err) = add_v2_reward_accounts_to_state(
242 &*self.consensus_handle,
243 &ViewNumber::new(*view),
244 accounts,
245 &merkle_tree,
246 leaf,
247 )
248 .await
249 {
250 tracing::warn!(?view, "Cannot update fetched account state: {err:#}");
251 }
252
253 Ok(Response::RewardAccountsV2(merkle_tree))
254 },
255
256 Request::RewardAccountsV1(height, view, accounts) => {
257 if let Some(state) = self.consensus_handle.state(ViewNumber::new(*view)).await
259 && let Ok(reward_accounts) = retain_v1_reward_accounts(
260 &state.reward_merkle_tree_v1,
261 accounts.iter().copied(),
262 )
263 {
264 return Ok(Response::RewardAccountsV1(reward_accounts));
265 }
266
267 let (merkle_tree, leaf) = self
269 .sql_storage("v1 reward accounts")?
270 .get_reward_accounts_v1(
271 &self.node_state,
272 *height,
273 ViewNumber::new(*view),
274 accounts,
275 )
276 .await
277 .with_context(|| "failed to get v1 reward accounts from sql storage")?;
278
279 if let Err(err) = add_v1_reward_accounts_to_state(
282 &*self.consensus_handle,
283 &ViewNumber::new(*view),
284 accounts,
285 &merkle_tree,
286 leaf,
287 )
288 .await
289 {
290 tracing::warn!(
291 ?view,
292 "Cannot update fetched v1 reward account state: {err:#}"
293 );
294 }
295
296 Ok(Response::RewardAccountsV1(merkle_tree))
297 },
298 Request::VidShare(block_number, _request_id) => {
299 let vid_share = match &self.storage {
301 Some(Storage::Sql(storage)) => storage
302 .get_vid_share(*block_number)
303 .await
304 .with_context(|| "failed to get vid share from sql storage")?,
305 Some(Storage::Fs(storage)) => {
306 let mut transaction = storage
308 .read()
309 .await
310 .with_context(|| "failed to open fs storage transaction")?;
311
312 transaction
314 .vid_share(BlockId::Number(*block_number as usize))
315 .await
316 .with_context(|| "failed to get vid share from fs storage")?
317 },
318 None => bail!("storage was not initialized"),
319 };
320
321 Ok(Response::VidShare(vid_share))
322 },
323 Request::StateCert(epoch) => {
324 let state_cert = self
325 .persistence
326 .get_state_cert_by_epoch(*epoch)
327 .await
328 .with_context(|| {
329 format!("failed to get state cert for epoch {epoch} from persistence")
330 })?;
331
332 match state_cert {
333 Some(cert) => Ok(Response::StateCert(cert)),
334 None => bail!("State certificate for epoch {epoch} not found"),
335 }
336 },
337 Request::Cert2(height) => {
338 let cert2 = self
339 .sql_storage("cert2")?
340 .load_cert2(*height)
341 .await
342 .with_context(|| "failed to load cert2 from sql storage")?;
343
344 match cert2 {
345 Some(cert2) => Ok(Response::Cert2(cert2)),
346 None => bail!("no cert2 available at height {height}"),
347 }
348 },
349 Request::RewardMerkleTreeV2(height, view) => {
350 if let Some(state) = self.consensus_handle.state(ViewNumber::new(*view)).await {
352 let tree_data =
353 TryInto::<RewardMerkleTreeV2Data>::try_into(&state.reward_merkle_tree_v2)
354 .inspect_err(|err| {
355 tracing::debug!(
356 %err, height, view,
357 "cannot serve reward merkle tree from memory"
358 )
359 })?;
360 let merkle_tree_bytes = bincode::serialize(&tree_data)
361 .context("Merkle tree serialization failed; this should never happen.")?;
362
363 return Ok(Response::RewardMerkleTreeV2(merkle_tree_bytes));
364 }
365
366 let merkle_tree_bytes = self
368 .sql_storage("reward merkle tree catchup")?
369 .load_tree(*height)
370 .await
371 .with_context(|| "failed to get reward merkle tree from sql storage")?;
372
373 Ok(Response::RewardMerkleTreeV2(merkle_tree_bytes))
374 },
375 }
376 }
377}
378
379async fn legacy_leaf_chain_from_memory<P: SequencerPersistence>(
384 consensus_handle: &ConsensusHandle<SeqTypes, Arc<P>>,
385 height: u64,
386) -> anyhow::Result<Vec<espresso_types::Leaf2>> {
387 let mut leaves = consensus_handle.undecided_leaves().await;
388 leaves.sort_by_key(|l| l.view_number());
389
390 let (position, mut last_leaf) = leaves
391 .iter()
392 .find_position(|l| l.height() == height)
393 .ok_or_else(|| anyhow::anyhow!("leaf at height {height} not in memory"))?;
394
395 let mut leaf_chain = vec![last_leaf.clone()];
396 for leaf in leaves.iter().skip(position + 1) {
397 if leaf.justify_qc().view_number() == last_leaf.view_number() {
398 leaf_chain.push(leaf.clone());
399 } else {
400 continue;
401 }
402 if leaf.view_number() == last_leaf.view_number() + 1 {
403 last_leaf = leaf;
404 break;
405 }
406 last_leaf = leaf;
407 }
408
409 for leaf in leaves
410 .iter()
411 .skip_while(|l| l.view_number() <= last_leaf.view_number())
412 {
413 if leaf.justify_qc().view_number() == last_leaf.view_number() {
414 leaf_chain.push(leaf.clone());
415 return Ok(leaf_chain);
416 }
417 }
418
419 anyhow::bail!("incomplete leaf chain in memory for height {height}")
420}
421
422pub fn retain_v2_reward_accounts(
426 state: &RewardMerkleTreeV2,
427 accounts: impl IntoIterator<Item = RewardAccountV2>,
428) -> anyhow::Result<RewardMerkleTreeV2> {
429 let mut snapshot = RewardMerkleTreeV2::from_commitment(state.commitment());
430 for account in accounts {
431 match state.universal_lookup(account) {
432 LookupResult::Ok(elem, proof) => {
433 snapshot.remember(account, elem, proof).unwrap();
436 },
437 LookupResult::NotFound(proof) => {
438 snapshot.non_membership_remember(account, proof).unwrap()
440 },
441 LookupResult::NotInMemory => {
442 bail!("missing account {account}");
443 },
444 }
445 }
446
447 Ok(snapshot)
448}
449
450pub fn retain_v1_reward_accounts(
454 state: &RewardMerkleTreeV1,
455 accounts: impl IntoIterator<Item = RewardAccountV1>,
456) -> anyhow::Result<RewardMerkleTreeV1> {
457 let mut snapshot = RewardMerkleTreeV1::from_commitment(state.commitment());
458 for account in accounts {
459 match state.universal_lookup(account) {
460 LookupResult::Ok(elem, proof) => {
461 snapshot.remember(account, elem, proof).unwrap();
464 },
465 LookupResult::NotFound(proof) => {
466 snapshot.non_membership_remember(account, proof).unwrap()
468 },
469 LookupResult::NotInMemory => {
470 bail!("missing account {account}");
471 },
472 }
473 }
474
475 Ok(snapshot)
476}