hotshot_query_service/data_source/fetching/
transaction.rs

1// Copyright (c) 2022 Espresso Systems (espressosys.com)
2// This file is part of the HotShot Query Service library.
3//
4// This program is free software: you can redistribute it and/or modify it under the terms of the GNU
5// General Public License as published by the Free Software Foundation, either version 3 of the
6// License, or (at your option) any later version.
7// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
8// even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9// General Public License for more details.
10// You should have received a copy of the GNU General Public License along with this program. If not,
11// see <https://www.gnu.org/licenses/>.
12
13//! Transaction fetching.
14
15use std::sync::Arc;
16
17use async_trait::async_trait;
18use derive_more::From;
19use futures::future::{BoxFuture, FutureExt};
20use hotshot_types::traits::node_implementation::NodeType;
21
22use super::{AvailabilityProvider, FetchRequest, Fetchable, Fetcher, Notifiers};
23use crate::{
24    availability::{QueryableHeader, QueryablePayload, TransactionHash, TransactionQueryData},
25    data_source::{
26        storage::{
27            pruning::PrunedHeightStorage, AvailabilityStorage, NodeStorage,
28            UpdateAvailabilityStorage,
29        },
30        update::VersionedDataSource,
31    },
32    Header, Payload, QueryResult,
33};
34
35#[derive(Clone, Copy, Debug, From)]
36pub(super) struct TransactionRequest<Types: NodeType>(TransactionHash<Types>);
37
38impl<Types: NodeType> FetchRequest for TransactionRequest<Types> {}
39
40#[async_trait]
41impl<Types> Fetchable<Types> for TransactionQueryData<Types>
42where
43    Types: NodeType,
44    Header<Types>: QueryableHeader<Types>,
45    Payload<Types>: QueryablePayload<Types>,
46{
47    type Request = TransactionRequest<Types>;
48
49    fn satisfies(&self, req: Self::Request) -> bool {
50        req.0 == self.hash()
51    }
52
53    async fn passive_fetch(
54        notifications: &Notifiers<Types>,
55        req: Self::Request,
56    ) -> BoxFuture<'static, Option<Self>> {
57        // Any block might satisfy the request; we won't know until we receive a new payload and
58        // check if it contains the desired transaction.
59        let wait_block = notifications
60            .block
61            .wait_for(move |block| block.transaction_by_hash(req.0).is_some())
62            .await;
63
64        async move {
65            let block = wait_block.await?;
66            Self::with_hash(&block, req.0)
67        }
68        .boxed()
69    }
70
71    async fn active_fetch<S, P>(
72        _tx: &mut impl AvailabilityStorage<Types>,
73        _fetcher: Arc<Fetcher<Types, S, P>>,
74        req: Self::Request,
75    ) -> anyhow::Result<()>
76    where
77        S: VersionedDataSource + 'static,
78        for<'a> S::Transaction<'a>: UpdateAvailabilityStorage<Types>,
79        for<'a> S::ReadOnly<'a>:
80            AvailabilityStorage<Types> + NodeStorage<Types> + PrunedHeightStorage,
81        P: AvailabilityProvider<Types>,
82    {
83        // We don't actively fetch transactions, because without a satisfying block payload, we have
84        // no way of knowing whether a block with such a transaction actually exists, and we don't
85        // want to bother peers with requests for non-existent transactions.
86        tracing::debug!("not fetching unknown transaction {req:?}");
87        Ok(())
88    }
89
90    async fn load<S>(storage: &mut S, req: Self::Request) -> QueryResult<Self>
91    where
92        S: AvailabilityStorage<Types>,
93    {
94        storage.get_transaction(req.0).await
95    }
96}