Skip to main content

qorechain/query/
typed.rs

1//! Typed query clients for the QoreChain modules that expose a gRPC `Query`
2//! service (crossvm, lightnode, pqc, qca, reputation, rlconsensus, svm).
3//!
4//! Rather than pull in a gRPC transport, the queries ride the chain RPC's
5//! `abci_query` method (the same JSON-RPC transport as [`JsonRpcClient`]): the
6//! ABCI path is the gRPC method name (`/qorechain.<module>.v1.Query/<Method>`),
7//! the request is the prost-encoded query message (hex), and the response value
8//! is the base64-encoded prost-encoded response message. Each method below
9//! returns the strongly typed prost response decoded from that value, so callers
10//! get real typed results without a gRPC dependency.
11
12use crate::error::{Error, Result};
13use crate::proto::qorechain;
14use crate::query::JsonRpcClient;
15use base64::engine::general_purpose::STANDARD as BASE64;
16use base64::Engine;
17use prost::Message;
18use serde_json::json;
19
20/// A typed query client over the chain RPC `abci_query` transport.
21#[derive(Debug, Clone)]
22pub struct TypedQueryClient {
23    rpc: JsonRpcClient,
24}
25
26impl TypedQueryClient {
27    /// Creates a typed query client targeting the chain RPC URL.
28    pub fn new(rpc_url: impl Into<String>) -> Self {
29        Self {
30            rpc: JsonRpcClient::new(rpc_url),
31        }
32    }
33
34    /// Wraps an existing [`JsonRpcClient`].
35    pub fn with_rpc(rpc: JsonRpcClient) -> Self {
36        Self { rpc }
37    }
38
39    /// Performs a typed ABCI gRPC query: encodes `req`, calls `abci_query` for
40    /// the gRPC `path`, and decodes the response value into `Resp`.
41    async fn grpc_query<Req: Message, Resp: Message + Default>(
42        &self,
43        path: &str,
44        req: &Req,
45    ) -> Result<Resp> {
46        let data_hex = hex::encode(req.encode_to_vec());
47        let params = json!({
48            "path": path,
49            "data": data_hex,
50            "prove": false,
51        });
52        let result = self.rpc.call("abci_query", params).await?;
53        let response = &result["response"];
54        // A non-zero ABCI code indicates a query error.
55        if let Some(code) = response["code"].as_u64() {
56            if code != 0 {
57                let log = response["log"].as_str().unwrap_or("query failed");
58                return Err(Error::InvalidResponse(format!(
59                    "abci_query {path} failed (code {code}): {log}"
60                )));
61            }
62        }
63        let value_b64 = response["value"].as_str().unwrap_or("");
64        let bytes = if value_b64.is_empty() {
65            Vec::new()
66        } else {
67            BASE64
68                .decode(value_b64)
69                .map_err(|e| Error::InvalidResponse(format!("decode abci value: {e}")))?
70        };
71        Resp::decode(bytes.as_slice())
72            .map_err(|e| Error::InvalidResponse(format!("decode {path} response: {e}")))
73    }
74
75    // --- pqc ---
76
77    /// Queries `qorechain.pqc.v1.Query/Account`.
78    pub async fn pqc_account(
79        &self,
80        address: impl Into<String>,
81    ) -> Result<qorechain::pqc::v1::QueryAccountResponse> {
82        self.grpc_query(
83            "/qorechain.pqc.v1.Query/Account",
84            &qorechain::pqc::v1::QueryAccountRequest {
85                address: address.into(),
86            },
87        )
88        .await
89    }
90
91    // --- crossvm ---
92
93    /// Queries `qorechain.crossvm.v1.Query/Params`.
94    pub async fn crossvm_params(&self) -> Result<qorechain::crossvm::v1::QueryParamsResponse> {
95        self.grpc_query(
96            "/qorechain.crossvm.v1.Query/Params",
97            &qorechain::crossvm::v1::QueryParamsRequest {},
98        )
99        .await
100    }
101
102    /// Queries `qorechain.crossvm.v1.Query/PendingMessages`.
103    pub async fn crossvm_pending_messages(
104        &self,
105    ) -> Result<qorechain::crossvm::v1::QueryPendingMessagesResponse> {
106        self.grpc_query(
107            "/qorechain.crossvm.v1.Query/PendingMessages",
108            &qorechain::crossvm::v1::QueryPendingMessagesRequest {},
109        )
110        .await
111    }
112
113    /// Queries `qorechain.crossvm.v1.Query/Message`.
114    pub async fn crossvm_message(
115        &self,
116        id: impl Into<String>,
117    ) -> Result<qorechain::crossvm::v1::QueryMessageResponse> {
118        self.grpc_query(
119            "/qorechain.crossvm.v1.Query/Message",
120            &qorechain::crossvm::v1::QueryMessageRequest { id: id.into() },
121        )
122        .await
123    }
124
125    // --- lightnode ---
126
127    /// Queries `qorechain.lightnode.v1.Query/LightNode`.
128    pub async fn lightnode(
129        &self,
130        address: impl Into<String>,
131    ) -> Result<qorechain::lightnode::v1::QueryLightNodeResponse> {
132        self.grpc_query(
133            "/qorechain.lightnode.v1.Query/LightNode",
134            &qorechain::lightnode::v1::QueryLightNodeRequest {
135                address: address.into(),
136            },
137        )
138        .await
139    }
140
141    /// Queries `qorechain.lightnode.v1.Query/LightNodes`.
142    pub async fn lightnodes(&self) -> Result<qorechain::lightnode::v1::QueryLightNodesResponse> {
143        self.grpc_query(
144            "/qorechain.lightnode.v1.Query/LightNodes",
145            &qorechain::lightnode::v1::QueryLightNodesRequest {},
146        )
147        .await
148    }
149
150    /// Queries `qorechain.lightnode.v1.Query/Params`.
151    pub async fn lightnode_params(&self) -> Result<qorechain::lightnode::v1::QueryParamsResponse> {
152        self.grpc_query(
153            "/qorechain.lightnode.v1.Query/Params",
154            &qorechain::lightnode::v1::QueryParamsRequest {},
155        )
156        .await
157    }
158
159    /// Queries `qorechain.lightnode.v1.Query/Rewards`.
160    pub async fn lightnode_rewards(
161        &self,
162        address: impl Into<String>,
163    ) -> Result<qorechain::lightnode::v1::QueryRewardsResponse> {
164        self.grpc_query(
165            "/qorechain.lightnode.v1.Query/Rewards",
166            &qorechain::lightnode::v1::QueryRewardsRequest {
167                address: address.into(),
168            },
169        )
170        .await
171    }
172
173    /// Queries `qorechain.lightnode.v1.Query/Stats`.
174    pub async fn lightnode_stats(&self) -> Result<qorechain::lightnode::v1::QueryStatsResponse> {
175        self.grpc_query(
176            "/qorechain.lightnode.v1.Query/Stats",
177            &qorechain::lightnode::v1::QueryStatsRequest {},
178        )
179        .await
180    }
181
182    // --- svm ---
183
184    /// Queries `qorechain.svm.v1.Query/Slot`.
185    pub async fn svm_slot(&self) -> Result<qorechain::svm::v1::QuerySlotResponse> {
186        self.grpc_query(
187            "/qorechain.svm.v1.Query/Slot",
188            &qorechain::svm::v1::QuerySlotRequest {},
189        )
190        .await
191    }
192
193    /// Queries `qorechain.svm.v1.Query/Account`.
194    pub async fn svm_account(
195        &self,
196        address: impl Into<String>,
197    ) -> Result<qorechain::svm::v1::QueryAccountResponse> {
198        self.grpc_query(
199            "/qorechain.svm.v1.Query/Account",
200            &qorechain::svm::v1::QueryAccountRequest {
201                address: address.into(),
202            },
203        )
204        .await
205    }
206
207    /// Queries `qorechain.svm.v1.Query/Program`.
208    pub async fn svm_program(
209        &self,
210        address: impl Into<String>,
211    ) -> Result<qorechain::svm::v1::QueryProgramResponse> {
212        self.grpc_query(
213            "/qorechain.svm.v1.Query/Program",
214            &qorechain::svm::v1::QueryProgramRequest {
215                address: address.into(),
216            },
217        )
218        .await
219    }
220
221    // --- reputation ---
222
223    /// Queries `qorechain.reputation.v1.Query/Params`.
224    pub async fn reputation_params(
225        &self,
226    ) -> Result<qorechain::reputation::v1::QueryParamsResponse> {
227        self.grpc_query(
228            "/qorechain.reputation.v1.Query/Params",
229            &qorechain::reputation::v1::QueryParamsRequest {},
230        )
231        .await
232    }
233
234    // --- qca ---
235
236    /// Queries `qorechain.qca.v1.Query/Config`.
237    pub async fn qca_config(&self) -> Result<qorechain::qca::v1::QueryConfigResponse> {
238        self.grpc_query(
239            "/qorechain.qca.v1.Query/Config",
240            &qorechain::qca::v1::QueryConfigRequest {},
241        )
242        .await
243    }
244
245    // --- rlconsensus ---
246
247    /// Queries `qorechain.rlconsensus.v1.Query/AgentStatus`.
248    pub async fn rlconsensus_agent_status(
249        &self,
250    ) -> Result<qorechain::rlconsensus::v1::QueryAgentStatusResponse> {
251        self.grpc_query(
252            "/qorechain.rlconsensus.v1.Query/AgentStatus",
253            &qorechain::rlconsensus::v1::QueryAgentStatusRequest {},
254        )
255        .await
256    }
257
258    /// Queries `qorechain.rlconsensus.v1.Query/Params`.
259    pub async fn rlconsensus_params(
260        &self,
261    ) -> Result<qorechain::rlconsensus::v1::QueryParamsResponse> {
262        self.grpc_query(
263            "/qorechain.rlconsensus.v1.Query/Params",
264            &qorechain::rlconsensus::v1::QueryParamsRequest {},
265        )
266        .await
267    }
268
269    /// Queries `qorechain.rlconsensus.v1.Query/Observation`.
270    pub async fn rlconsensus_observation(
271        &self,
272    ) -> Result<qorechain::rlconsensus::v1::QueryObservationResponse> {
273        self.grpc_query(
274            "/qorechain.rlconsensus.v1.Query/Observation",
275            &qorechain::rlconsensus::v1::QueryObservationRequest {},
276        )
277        .await
278    }
279
280    /// Queries `qorechain.rlconsensus.v1.Query/Reward`.
281    pub async fn rlconsensus_reward(
282        &self,
283    ) -> Result<qorechain::rlconsensus::v1::QueryRewardResponse> {
284        self.grpc_query(
285            "/qorechain.rlconsensus.v1.Query/Reward",
286            &qorechain::rlconsensus::v1::QueryRewardRequest {},
287        )
288        .await
289    }
290
291    /// Queries `qorechain.rlconsensus.v1.Query/Policy`.
292    pub async fn rlconsensus_policy(
293        &self,
294    ) -> Result<qorechain::rlconsensus::v1::QueryPolicyResponse> {
295        self.grpc_query(
296            "/qorechain.rlconsensus.v1.Query/Policy",
297            &qorechain::rlconsensus::v1::QueryPolicyRequest {},
298        )
299        .await
300    }
301}