snarkvm_ledger_query/query/
static_.rs1use crate::QueryTrait;
17
18use snarkvm_console::{network::prelude::*, program::StatePath, types::Field};
19
20use anyhow::{Context, Result, ensure};
21use serde::Deserialize;
22use std::{collections::HashMap, str::FromStr};
23
24#[derive(Clone)]
25pub struct StaticQuery<N: Network> {
26 block_height: u32,
27 state_root: N::StateRoot,
28 state_paths: HashMap<Field<N>, StatePath<N>>,
29}
30
31impl<N: Network> StaticQuery<N> {
32 pub fn new(block_height: u32, state_root: N::StateRoot, state_paths: HashMap<Field<N>, StatePath<N>>) -> Self {
33 Self { block_height, state_root, state_paths }
34 }
35}
36
37#[derive(Deserialize)]
38struct StaticQueryInput {
39 state_root: String,
40 height: u32,
41}
42
43impl<N: Network> FromStr for StaticQuery<N> {
44 type Err = anyhow::Error;
45
46 fn from_str(s: &str) -> Result<Self> {
47 ensure!(s.trim().starts_with('{'), "Not a static query");
48
49 let input: StaticQueryInput = serde_json::from_str(s).with_context(|| "Invalid JSON format in static query")?;
50 let state_root = N::StateRoot::from_str(&input.state_root).map_err(|_| anyhow!("Invalid state root format"))?;
51
52 Ok(Self { state_root, block_height: input.height, state_paths: HashMap::new() })
53 }
54}
55
56#[cfg_attr(feature = "async", async_trait::async_trait(?Send))]
57impl<N: Network> QueryTrait<N> for StaticQuery<N> {
58 fn current_state_root(&self) -> Result<N::StateRoot> {
60 Ok(self.state_root)
61 }
62
63 #[cfg(feature = "async")]
65 async fn current_state_root_async(&self) -> Result<N::StateRoot> {
66 self.current_state_root()
68 }
69
70 fn get_state_path_for_commitment(&self, commitment: &Field<N>) -> Result<StatePath<N>> {
72 match self.state_paths.get(commitment) {
73 Some(state_path) => Ok(state_path.clone()),
74 None => bail!("Could not find state path for commitment '{commitment}'"),
75 }
76 }
77
78 #[cfg(feature = "async")]
80 async fn get_state_path_for_commitment_async(&self, commitment: &Field<N>) -> Result<StatePath<N>> {
81 self.get_state_path_for_commitment(commitment)
83 }
84
85 fn get_state_paths_for_commitments(&self, commitments: &[Field<N>]) -> Result<Vec<StatePath<N>>> {
87 commitments
88 .iter()
89 .map(|commitment| self.get_state_path_for_commitment(commitment))
90 .collect::<Result<Vec<StatePath<N>>>>()
91 }
92
93 #[cfg(feature = "async")]
95 async fn get_state_paths_for_commitments_async(&self, commitments: &[Field<N>]) -> Result<Vec<StatePath<N>>> {
96 self.get_state_paths_for_commitments(commitments)
98 }
99
100 fn current_block_height(&self) -> Result<u32> {
102 Ok(self.block_height)
103 }
104
105 #[cfg(feature = "async")]
107 async fn current_block_height_async(&self) -> Result<u32> {
108 self.current_block_height()
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116 use snarkvm_console::network::TestnetV0;
117
118 #[test]
119 fn test_static_query_parse() {
120 let json = r#"{"state_root": "sr1dz06ur5spdgzkguh4pr42mvft6u3nwsg5drh9rdja9v8jpcz3czsls9geg", "height": 14}"#
121 .to_string();
122 let query: Result<StaticQuery<TestnetV0>> = json.parse();
123 assert!(query.is_ok());
124 }
125
126 #[test]
127 fn test_static_query_parse_invalid() {
128 let json = r#"{"invalid_key": "sr1dz06ur5spdgzkguh4pr42mvft6u3nwsg5drh9rdja9v8jpcz3czsls9geg", "height": 14}"#
129 .to_string();
130 let query: Result<StaticQuery<TestnetV0>> = json.parse();
131 assert!(query.is_err());
132 }
133}