snarkvm_ledger_query/
query.rs

1// Copyright 2024 Aleo Network Foundation
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use crate::QueryTrait;
17use console::{
18    network::prelude::*,
19    program::{ProgramID, StatePath},
20    types::Field,
21};
22use ledger_store::{BlockStorage, BlockStore};
23use synthesizer_program::Program;
24
25#[derive(Clone)]
26pub enum Query<N: Network, B: BlockStorage<N>> {
27    /// The block store from the VM.
28    VM(BlockStore<N, B>),
29    /// The base URL of the node.
30    REST(String),
31}
32
33impl<N: Network, B: BlockStorage<N>> From<BlockStore<N, B>> for Query<N, B> {
34    fn from(block_store: BlockStore<N, B>) -> Self {
35        Self::VM(block_store)
36    }
37}
38
39impl<N: Network, B: BlockStorage<N>> From<&BlockStore<N, B>> for Query<N, B> {
40    fn from(block_store: &BlockStore<N, B>) -> Self {
41        Self::VM(block_store.clone())
42    }
43}
44
45impl<N: Network, B: BlockStorage<N>> From<String> for Query<N, B> {
46    fn from(url: String) -> Self {
47        Self::REST(url)
48    }
49}
50
51impl<N: Network, B: BlockStorage<N>> From<&String> for Query<N, B> {
52    fn from(url: &String) -> Self {
53        Self::REST(url.to_string())
54    }
55}
56
57impl<N: Network, B: BlockStorage<N>> From<&str> for Query<N, B> {
58    fn from(url: &str) -> Self {
59        Self::REST(url.to_string())
60    }
61}
62
63#[cfg_attr(feature = "async", async_trait(?Send))]
64impl<N: Network, B: BlockStorage<N>> QueryTrait<N> for Query<N, B> {
65    /// Returns the current state root.
66    fn current_state_root(&self) -> Result<N::StateRoot> {
67        match self {
68            Self::VM(block_store) => Ok(block_store.current_state_root()),
69            Self::REST(url) => match N::ID {
70                console::network::MainnetV0::ID => {
71                    Ok(Self::get_request(&format!("{url}/mainnet/stateRoot/latest"))?.into_json()?)
72                }
73                console::network::TestnetV0::ID => {
74                    Ok(Self::get_request(&format!("{url}/testnet/stateRoot/latest"))?.into_json()?)
75                }
76                console::network::CanaryV0::ID => {
77                    Ok(Self::get_request(&format!("{url}/canary/stateRoot/latest"))?.into_json()?)
78                }
79                _ => bail!("Unsupported network ID in inclusion query"),
80            },
81        }
82    }
83
84    /// Returns the current state root.
85    #[cfg(feature = "async")]
86    async fn current_state_root_async(&self) -> Result<N::StateRoot> {
87        match self {
88            Self::VM(block_store) => Ok(block_store.current_state_root()),
89            Self::REST(url) => match N::ID {
90                console::network::MainnetV0::ID => {
91                    Ok(Self::get_request_async(&format!("{url}/mainnet/stateRoot/latest")).await?.json().await?)
92                }
93                console::network::TestnetV0::ID => {
94                    Ok(Self::get_request_async(&format!("{url}/testnet/stateRoot/latest")).await?.json().await?)
95                }
96                console::network::CanaryV0::ID => {
97                    Ok(Self::get_request_async(&format!("{url}/canary/stateRoot/latest")).await?.json().await?)
98                }
99                _ => bail!("Unsupported network ID in inclusion query"),
100            },
101        }
102    }
103
104    /// Returns a state path for the given `commitment`.
105    fn get_state_path_for_commitment(&self, commitment: &Field<N>) -> Result<StatePath<N>> {
106        match self {
107            Self::VM(block_store) => block_store.get_state_path_for_commitment(commitment),
108            Self::REST(url) => match N::ID {
109                console::network::MainnetV0::ID => {
110                    Ok(Self::get_request(&format!("{url}/mainnet/statePath/{commitment}"))?.into_json()?)
111                }
112                console::network::TestnetV0::ID => {
113                    Ok(Self::get_request(&format!("{url}/testnet/statePath/{commitment}"))?.into_json()?)
114                }
115                console::network::CanaryV0::ID => {
116                    Ok(Self::get_request(&format!("{url}/canary/statePath/{commitment}"))?.into_json()?)
117                }
118                _ => bail!("Unsupported network ID in inclusion query"),
119            },
120        }
121    }
122
123    /// Returns a state path for the given `commitment`.
124    #[cfg(feature = "async")]
125    async fn get_state_path_for_commitment_async(&self, commitment: &Field<N>) -> Result<StatePath<N>> {
126        match self {
127            Self::VM(block_store) => block_store.get_state_path_for_commitment(commitment),
128            Self::REST(url) => match N::ID {
129                console::network::MainnetV0::ID => {
130                    Ok(Self::get_request_async(&format!("{url}/mainnet/statePath/{commitment}")).await?.json().await?)
131                }
132                console::network::TestnetV0::ID => {
133                    Ok(Self::get_request_async(&format!("{url}/testnet/statePath/{commitment}")).await?.json().await?)
134                }
135                console::network::CanaryV0::ID => {
136                    Ok(Self::get_request_async(&format!("{url}/canary/statePath/{commitment}")).await?.json().await?)
137                }
138                _ => bail!("Unsupported network ID in inclusion query"),
139            },
140        }
141    }
142
143    /// Returns a state path for the given `commitment`.
144    fn current_block_height(&self) -> Result<u32> {
145        match self {
146            Self::VM(block_store) => Ok(block_store.max_height().unwrap_or_default()),
147            Self::REST(url) => match N::ID {
148                console::network::MainnetV0::ID => {
149                    Ok(Self::get_request(&format!("{url}/mainnet/block/height/latest"))?.into_json()?)
150                }
151                console::network::TestnetV0::ID => {
152                    Ok(Self::get_request(&format!("{url}/testnet/block/height/latest"))?.into_json()?)
153                }
154                console::network::CanaryV0::ID => {
155                    Ok(Self::get_request(&format!("{url}/canary/block/height/latest"))?.into_json()?)
156                }
157                _ => bail!("Unsupported network ID in inclusion query"),
158            },
159        }
160    }
161
162    /// Returns a state path for the given `commitment`.
163    #[cfg(feature = "async")]
164    async fn current_block_height_async(&self) -> Result<u32> {
165        match self {
166            Self::VM(block_store) => Ok(block_store.max_height().unwrap_or_default()),
167            Self::REST(url) => match N::ID {
168                console::network::MainnetV0::ID => {
169                    Ok(Self::get_request_async(&format!("{url}/mainnet/block/height/latest")).await?.json().await?)
170                }
171                console::network::TestnetV0::ID => {
172                    Ok(Self::get_request_async(&format!("{url}/testnet/block/height/latest")).await?.json().await?)
173                }
174                console::network::CanaryV0::ID => {
175                    Ok(Self::get_request_async(&format!("{url}/canary/block/height/latest")).await?.json().await?)
176                }
177                _ => bail!("Unsupported network ID in inclusion query"),
178            },
179        }
180    }
181}
182
183impl<N: Network, B: BlockStorage<N>> Query<N, B> {
184    /// Returns the program for the given program ID.
185    pub fn get_program(&self, program_id: &ProgramID<N>) -> Result<Program<N>> {
186        match self {
187            Self::VM(block_store) => {
188                block_store.get_program(program_id)?.ok_or_else(|| anyhow!("Program {program_id} not found in storage"))
189            }
190            Self::REST(url) => match N::ID {
191                console::network::MainnetV0::ID => {
192                    Ok(Self::get_request(&format!("{url}/mainnet/program/{program_id}"))?.into_json()?)
193                }
194                console::network::TestnetV0::ID => {
195                    Ok(Self::get_request(&format!("{url}/testnet/program/{program_id}"))?.into_json()?)
196                }
197                console::network::CanaryV0::ID => {
198                    Ok(Self::get_request(&format!("{url}/canary/program/{program_id}"))?.into_json()?)
199                }
200                _ => bail!("Unsupported network ID in inclusion query"),
201            },
202        }
203    }
204
205    /// Returns the program for the given program ID.
206    #[cfg(feature = "async")]
207    pub async fn get_program_async(&self, program_id: &ProgramID<N>) -> Result<Program<N>> {
208        match self {
209            Self::VM(block_store) => {
210                block_store.get_program(program_id)?.ok_or_else(|| anyhow!("Program {program_id} not found in storage"))
211            }
212            Self::REST(url) => match N::ID {
213                console::network::MainnetV0::ID => {
214                    Ok(Self::get_request_async(&format!("{url}/mainnet/program/{program_id}")).await?.json().await?)
215                }
216                console::network::TestnetV0::ID => {
217                    Ok(Self::get_request_async(&format!("{url}/testnet/program/{program_id}")).await?.json().await?)
218                }
219                console::network::CanaryV0::ID => {
220                    Ok(Self::get_request_async(&format!("{url}/canary/program/{program_id}")).await?.json().await?)
221                }
222                _ => bail!("Unsupported network ID in inclusion query"),
223            },
224        }
225    }
226
227    /// Performs a GET request to the given URL.
228    fn get_request(url: &str) -> Result<ureq::Response> {
229        let response = ureq::get(url).call()?;
230        if response.status() == 200 { Ok(response) } else { bail!("Failed to fetch from {url}") }
231    }
232
233    /// Performs a GET request to the given URL.
234    #[cfg(feature = "async")]
235    async fn get_request_async(url: &str) -> Result<reqwest::Response> {
236        let response = reqwest::get(url).await?;
237        if response.status() == 200 { Ok(response) } else { bail!("Failed to fetch from {url}") }
238    }
239}