use pasta_curves::Fp;
use vote_commitment_tree::sync_api::{BlockCommitments, TreeState, TreeSyncApi};
use crate::types::{
QueryCommitmentLeavesResponse, QueryCommitmentTreeResponse, QueryLatestTreeResponse,
};
#[derive(Debug, thiserror::Error)]
pub enum HttpSyncError {
#[error("HTTP request failed: {0}")]
Http(#[from] reqwest::Error),
#[error("parse error: {0}")]
Parse(#[from] crate::types::ParseError),
#[error("server returned no tree state")]
NoTreeState,
}
pub struct HttpTreeSyncApi {
client: reqwest::blocking::Client,
base_url: String,
round_id: String,
}
impl HttpTreeSyncApi {
pub fn new(base_url: impl Into<String>, round_id: impl Into<String>) -> Self {
Self {
client: reqwest::blocking::Client::new(),
base_url: base_url.into(),
round_id: round_id.into(),
}
}
pub fn with_client(
client: reqwest::blocking::Client,
base_url: impl Into<String>,
round_id: impl Into<String>,
) -> Self {
Self {
client,
base_url: base_url.into(),
round_id: round_id.into(),
}
}
}
impl TreeSyncApi for HttpTreeSyncApi {
type Error = HttpSyncError;
fn get_tree_state(&self) -> Result<TreeState, Self::Error> {
let url = format!(
"{}/shielded-vote/v1/commitment-tree/{}/latest",
self.base_url, self.round_id
);
let resp: QueryLatestTreeResponse = self.client.get(&url).send()?.json()?;
resp.tree
.ok_or(HttpSyncError::NoTreeState)?
.into_tree_state()
.map_err(HttpSyncError::Parse)
}
fn get_root_at_height(&self, height: u32) -> Result<Option<Fp>, Self::Error> {
let url = format!(
"{}/shielded-vote/v1/commitment-tree/{}/{}",
self.base_url, self.round_id, height
);
let resp: QueryCommitmentTreeResponse = self.client.get(&url).send()?.json()?;
match resp.tree {
Some(state) => {
let ts = state.into_tree_state().map_err(HttpSyncError::Parse)?;
Ok(Some(ts.root))
}
None => Ok(None),
}
}
fn get_block_commitments(
&self,
from_height: u32,
to_height: u32,
) -> Result<Vec<BlockCommitments>, Self::Error> {
let url = format!(
"{}/shielded-vote/v1/commitment-tree/{}/leaves?from_height={}&to_height={}",
self.base_url, self.round_id, from_height, to_height
);
let resp: QueryCommitmentLeavesResponse = self.client.get(&url).send()?.json()?;
resp.blocks
.into_iter()
.map(|b| b.into_block_commitments().map_err(HttpSyncError::Parse))
.collect()
}
}