use crate::QueryTrait;
use console::{
network::prelude::*,
program::{ProgramID, StatePath},
types::Field,
};
use ledger_store::{BlockStorage, BlockStore};
use synthesizer_program::Program;
#[derive(Clone)]
pub enum Query<N: Network, B: BlockStorage<N>> {
VM(BlockStore<N, B>),
REST(String),
}
impl<N: Network, B: BlockStorage<N>> From<BlockStore<N, B>> for Query<N, B> {
fn from(block_store: BlockStore<N, B>) -> Self {
Self::VM(block_store)
}
}
impl<N: Network, B: BlockStorage<N>> From<&BlockStore<N, B>> for Query<N, B> {
fn from(block_store: &BlockStore<N, B>) -> Self {
Self::VM(block_store.clone())
}
}
impl<N: Network, B: BlockStorage<N>> From<String> for Query<N, B> {
fn from(url: String) -> Self {
Self::REST(url)
}
}
impl<N: Network, B: BlockStorage<N>> From<&String> for Query<N, B> {
fn from(url: &String) -> Self {
Self::REST(url.to_string())
}
}
impl<N: Network, B: BlockStorage<N>> From<&str> for Query<N, B> {
fn from(url: &str) -> Self {
Self::REST(url.to_string())
}
}
#[cfg_attr(feature = "async", async_trait(?Send))]
impl<N: Network, B: BlockStorage<N>> QueryTrait<N> for Query<N, B> {
fn current_state_root(&self) -> Result<N::StateRoot> {
match self {
Self::VM(block_store) => Ok(block_store.current_state_root()),
Self::REST(url) => match N::ID {
3 => Ok(Self::get_request(&format!("{url}/testnet3/latest/stateRoot"))?.into_json()?),
_ => bail!("Unsupported network ID in inclusion query"),
},
}
}
#[cfg(feature = "async")]
async fn current_state_root_async(&self) -> Result<N::StateRoot> {
match self {
Self::VM(block_store) => Ok(block_store.current_state_root()),
Self::REST(url) => match N::ID {
3 => Ok(Self::get_request_async(&format!("{url}/testnet3/latest/stateRoot")).await?.json().await?),
_ => bail!("Unsupported network ID in inclusion query"),
},
}
}
fn get_state_path_for_commitment(&self, commitment: &Field<N>) -> Result<StatePath<N>> {
match self {
Self::VM(block_store) => block_store.get_state_path_for_commitment(commitment),
Self::REST(url) => match N::ID {
3 => Ok(Self::get_request(&format!("{url}/testnet3/statePath/{commitment}"))?.into_json()?),
_ => bail!("Unsupported network ID in inclusion query"),
},
}
}
#[cfg(feature = "async")]
async fn get_state_path_for_commitment_async(&self, commitment: &Field<N>) -> Result<StatePath<N>> {
match self {
Self::VM(block_store) => block_store.get_state_path_for_commitment(commitment),
Self::REST(url) => match N::ID {
3 => {
Ok(Self::get_request_async(&format!("{url}/testnet3/statePath/{commitment}")).await?.json().await?)
}
_ => bail!("Unsupported network ID in inclusion query"),
},
}
}
}
impl<N: Network, B: BlockStorage<N>> Query<N, B> {
pub fn get_program(&self, program_id: &ProgramID<N>) -> Result<Program<N>> {
match self {
Self::VM(block_store) => {
block_store.get_program(program_id)?.ok_or_else(|| anyhow!("Program {program_id} not found in storage"))
}
Self::REST(url) => match N::ID {
3 => Ok(Self::get_request(&format!("{url}/testnet3/program/{program_id}"))?.into_json()?),
_ => bail!("Unsupported network ID in inclusion query"),
},
}
}
#[cfg(feature = "async")]
pub async fn get_program_async(&self, program_id: &ProgramID<N>) -> Result<Program<N>> {
match self {
Self::VM(block_store) => {
block_store.get_program(program_id)?.ok_or_else(|| anyhow!("Program {program_id} not found in storage"))
}
Self::REST(url) => match N::ID {
3 => Ok(Self::get_request_async(&format!("{url}/testnet3/program/{program_id}")).await?.json().await?),
_ => bail!("Unsupported network ID in inclusion query"),
},
}
}
fn get_request(url: &str) -> Result<ureq::Response> {
let response = ureq::get(url).call()?;
if response.status() == 200 { Ok(response) } else { bail!("Failed to fetch from {url}") }
}
#[cfg(feature = "async")]
async fn get_request_async(url: &str) -> Result<reqwest::Response> {
let response = reqwest::get(url).await?;
if response.status() == 200 { Ok(response) } else { bail!("Failed to fetch from {url}") }
}
}