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