hedera/query/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use futures_core::future::BoxFuture;
4use time::Duration;
5
6use crate::execute::execute;
7use crate::query::cost::QueryCost;
8use crate::query::payment_transaction::PaymentTransaction;
9use crate::{
10    AccountId,
11    Client,
12    Error,
13    Hbar,
14    TransactionId,
15    TransactionReceiptQuery,
16};
17
18mod any;
19mod cost;
20mod execute;
21pub(super) mod payment_transaction;
22mod protobuf;
23
24pub(crate) use any::AnyQueryData;
25pub use any::{
26    AnyQuery,
27    AnyQueryResponse,
28};
29pub(crate) use execute::{
30    response_header,
31    QueryExecute,
32};
33pub(crate) use protobuf::ToQueryProtobuf;
34
35/// A query that can be executed on the Hiero network.
36#[derive(Debug, Default)]
37pub struct Query<D>
38where
39    D: QueryExecute,
40{
41    pub(crate) data: D,
42    pub(crate) payment: PaymentTransaction,
43}
44
45impl<D> Query<D>
46where
47    D: QueryExecute + Default,
48{
49    /// Create a new query ready for configuration and execution.
50    #[inline]
51    #[must_use]
52    pub fn new() -> Self {
53        Self::default()
54    }
55}
56
57impl<D> Query<D>
58where
59    D: QueryExecute,
60{
61    /// Returns the account IDs of the nodes that this query may be submitted to.
62    ///
63    /// Defaults to the full list of nodes configured on the client; or, the node account IDs
64    /// configured on the query payment transaction (if explicitly provided).
65    #[must_use]
66    pub fn get_node_account_ids(&self) -> Option<&[AccountId]> {
67        self.payment.get_node_account_ids()
68    }
69
70    /// Sets the account IDs of the nodes that this query may be submitted to.
71    ///
72    /// Defaults to the full list of nodes configured on the client; or, the node account IDs
73    /// configured on the query payment transaction (if explicitly provided).
74    pub fn node_account_ids(&mut self, ids: impl IntoIterator<Item = AccountId>) -> &mut Self {
75        self.payment.node_account_ids(ids);
76        self
77    }
78
79    /// Returns the explicit payment amount for this query.
80    ///
81    /// The client will submit exactly this amount for the payment of this query. Hiero
82    /// will not return any remainder (over the actual cost for this query).
83    #[must_use]
84    pub fn get_payment_amount(&self) -> Option<Hbar> {
85        self.payment.get_amount()
86    }
87
88    /// Sets the explicit payment amount for this query.
89    ///
90    /// The client will submit exactly this amount for the payment of this query. Hiero
91    /// will not return any remainder (over the actual cost for this query).
92    pub fn payment_amount(&mut self, amount: Hbar) -> &mut Self {
93        self.payment.amount(amount);
94        self
95    }
96
97    /// Returns the maximum payment allowable for this query.
98    #[must_use]
99    pub fn get_max_amount(&self) -> Option<Hbar> {
100        self.payment.get_max_amount()
101    }
102
103    /// Sets the maximum payment allowable for this query.
104    ///
105    /// When a query is executed without an explicit payment amount set,
106    /// the client will first request the cost of the given query from the node it will be
107    /// submitted to and attach a payment for that amount from the operator account on the client.
108    ///
109    /// If the returned value is greater than this value, a [`MaxQueryPaymentExceeded`](crate::Error::MaxQueryPaymentExceeded) error
110    /// will be returned.
111    ///
112    /// Defaults to the maximum payment amount configured on the client.
113    ///
114    /// Sets to `None` to allow unlimited payment amounts.
115    pub fn max_payment_amount(&mut self, max: impl Into<Option<Hbar>>) -> &mut Self {
116        self.payment.max_amount(max);
117        self
118    }
119
120    /// Returns the duration that the payment transaction is valid for, once finalized and signed.
121    #[must_use]
122    pub fn get_payment_transaction_valid_duration(&self) -> Option<Duration> {
123        self.payment.get_transaction_valid_duration()
124    }
125
126    /// Sets the duration that the payment transaction is valid for, once finalized and signed.
127    ///
128    /// Defaults to 120 seconds (or two minutes).
129    pub fn payment_transaction_valid_duration(&mut self, duration: Duration) -> &mut Self {
130        self.payment.transaction_valid_duration(duration);
131        self
132    }
133
134    /// Returns the maximum transaction fee the payer account is willing to pay
135    /// for the query payment transaction.
136    #[must_use]
137    pub fn get_max_payment_transaction_fee(&self) -> Option<Hbar> {
138        self.payment.get_max_transaction_fee()
139    }
140
141    /// Sets the maximum transaction fee the payer account is willing to pay for the query
142    /// payment transaction.
143    ///
144    /// Defaults to 1 hbar.
145    pub fn max_payment_transaction_fee(&mut self, fee: Hbar) -> &mut Self {
146        self.payment.max_transaction_fee(fee);
147        self
148    }
149
150    /// Returns the note / description that should be recorded in the transaction record for the payment transaction.
151    #[must_use]
152    pub fn get_payment_transaction_memo(&self) -> &str {
153        self.payment.get_transaction_memo()
154    }
155
156    /// Sets a note / description that should be recorded in the transaction record for the payment transaction.
157    ///
158    /// Maximum length of 100 characters.
159    pub fn payment_transaction_memo(&mut self, memo: impl AsRef<str>) -> &mut Self {
160        self.payment.transaction_memo(memo);
161        self
162    }
163
164    /// Returns the explicit transaction ID used to identify this query's payment transaction, if set
165    /// .
166    #[must_use]
167    pub fn get_payment_transaction_id(&self) -> Option<TransactionId> {
168        self.payment.get_transaction_id()
169    }
170
171    /// Sets an explicit transaction ID to use to identify the payment transaction
172    /// on this query.
173    ///
174    /// Overrides payer account defined on this query or on the client.
175    pub fn payment_transaction_id(&mut self, id: TransactionId) -> &mut Self {
176        self.payment.transaction_id(id);
177        self
178    }
179
180    /// Fetch the cost of this query.
181    pub async fn get_cost(&self, client: &Client) -> crate::Result<Hbar> {
182        self.get_cost_with_optional_timeout(client, None).await
183    }
184
185    pub(crate) async fn get_cost_with_optional_timeout(
186        &self,
187        client: &Client,
188        timeout: Option<std::time::Duration>,
189    ) -> crate::Result<Hbar> {
190        if !self.data.is_payment_required() {
191            return Ok(Hbar::ZERO);
192        }
193
194        QueryCost::new(self).execute(client, timeout).await
195    }
196
197    /// Fetch the cost of this query.
198    pub async fn get_cost_with_timeout(
199        &self,
200        client: &Client,
201        timeout: std::time::Duration,
202    ) -> crate::Result<Hbar> {
203        self.get_cost_with_optional_timeout(client, Some(timeout)).await
204    }
205}
206
207impl<D> Query<D>
208where
209    D: QueryExecute,
210{
211    /// Execute this query against the provided client of the Hiero network.
212    // todo:
213    #[allow(clippy::missing_errors_doc)]
214    pub async fn execute(&mut self, client: &Client) -> crate::Result<D::Response> {
215        self.execute_with_optional_timeout(client, None).await
216    }
217
218    // eww long name
219    pub(crate) async fn execute_with_optional_timeout(
220        &mut self,
221        client: &Client,
222        timeout: Option<std::time::Duration>,
223    ) -> crate::Result<D::Response> {
224        fn recurse_receipt(
225            transaction_id: &TransactionId,
226            client: Client,
227            timeout: Option<std::time::Duration>,
228        ) -> BoxFuture<'_, ()> {
229            Box::pin(async move {
230                let _ = TransactionReceiptQuery::new()
231                    .transaction_id(*transaction_id)
232                    .execute_with_optional_timeout(&client, timeout)
233                    .await;
234            })
235        }
236
237        // hack: this is a TransactionRecordQuery, which means we need to run the receipt first.
238        if let Some(transaction_id) = self.data.transaction_id() {
239            if self.data.is_payment_required() {
240                let client = client.clone();
241                recurse_receipt(&transaction_id, client, timeout).await;
242            }
243        }
244
245        if self.payment.get_amount().is_none() && self.data.is_payment_required() {
246            // should this inherit the timeout?
247            // payment is required but none was specified, query the cost
248            let cost = QueryCost::new(self).execute(client, None).await?;
249
250            if self.payment.get_max_amount().is_none() {
251                // N.B. This can still be `None`.
252                self.payment.max_amount(client.default_max_query_payment());
253            }
254
255            if let Some(max_amount) = self.payment.get_max_amount() {
256                if cost > max_amount {
257                    return Err(Error::MaxQueryPaymentExceeded {
258                        query_cost: cost,
259                        max_query_payment: max_amount,
260                    });
261                }
262            }
263
264            self.payment.amount(cost);
265        }
266
267        if self.data.is_payment_required() {
268            self.payment.freeze_with(client)?;
269        }
270
271        execute(client, self, timeout).await
272    }
273
274    /// Execute this query against the provided client of the Hiero network.
275    // todo:
276    #[allow(clippy::missing_errors_doc)]
277    pub async fn execute_with_timeout(
278        &mut self,
279        client: &Client,
280        timeout: std::time::Duration,
281    ) -> crate::Result<D::Response> {
282        self.execute_with_optional_timeout(client, Some(timeout)).await
283    }
284}