use std::{
collections::{BTreeMap, HashMap},
net::SocketAddr,
str,
};
use futures::{future::BoxFuture, FutureExt};
use http::Response;
use hyper::Body;
use semver::Version;
use serde::{Deserialize, Serialize};
use tracing::info;
use warp_json_rpc::Builder;
use super::{
ApiRequest, Error, ErrorCode, ReactorEventT, RpcWithParams, RpcWithParamsExt, RpcWithoutParams,
RpcWithoutParamsExt,
};
use crate::{
components::{api_server::CLIENT_API_VERSION, consensus::EraId, small_network::NodeId},
effect::EffectBuilder,
reactor::QueueKind,
types::{
json_compatibility::ExecutionResult, Block, BlockHash, Deploy, DeployHash, StatusFeed,
Timestamp,
},
};
#[derive(Serialize, Deserialize, Debug)]
pub struct GetDeployParams {
pub deploy_hash: DeployHash,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct JsonExecutionResult {
pub block_hash: BlockHash,
pub result: ExecutionResult,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct GetDeployResult {
pub api_version: Version,
pub deploy: Deploy,
pub execution_results: Vec<JsonExecutionResult>,
}
pub struct GetDeploy {}
impl RpcWithParams for GetDeploy {
const METHOD: &'static str = "info_get_deploy";
type RequestParams = GetDeployParams;
type ResponseResult = GetDeployResult;
}
impl RpcWithParamsExt for GetDeploy {
fn handle_request<REv: ReactorEventT>(
effect_builder: EffectBuilder<REv>,
response_builder: Builder,
params: Self::RequestParams,
) -> BoxFuture<'static, Result<Response<Body>, Error>> {
async move {
let maybe_deploy_and_metadata = effect_builder
.make_request(
|responder| ApiRequest::GetDeploy {
hash: params.deploy_hash,
responder,
},
QueueKind::Api,
)
.await;
let (deploy, metadata) = match maybe_deploy_and_metadata {
Some((deploy, metadata)) => (deploy, metadata),
None => {
info!(
"failed to get {} and metadata from storage",
params.deploy_hash
);
return Ok(response_builder.error(warp_json_rpc::Error::custom(
ErrorCode::NoSuchDeploy as i64,
"deploy not known",
))?);
}
};
let execution_results = metadata
.execution_results
.into_iter()
.map(|(block_hash, result)| JsonExecutionResult { block_hash, result })
.collect();
let result = Self::ResponseResult {
api_version: CLIENT_API_VERSION.clone(),
deploy,
execution_results,
};
Ok(response_builder.success(result)?)
}
.boxed()
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct GetPeersResult {
pub api_version: Version,
pub peers: BTreeMap<String, SocketAddr>,
}
pub struct GetPeers {}
impl RpcWithoutParams for GetPeers {
const METHOD: &'static str = "info_get_peers";
type ResponseResult = GetPeersResult;
}
impl RpcWithoutParamsExt for GetPeers {
fn handle_request<REv: ReactorEventT>(
effect_builder: EffectBuilder<REv>,
response_builder: Builder,
) -> BoxFuture<'static, Result<Response<Body>, Error>> {
async move {
let peers = effect_builder
.make_request(
|responder| ApiRequest::GetPeers { responder },
QueueKind::Api,
)
.await;
let peers = peers_hashmap_to_btreemap(peers);
let result = Self::ResponseResult {
api_version: CLIENT_API_VERSION.clone(),
peers,
};
Ok(response_builder.success(result)?)
}
.boxed()
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct MinimalBlockInfo {
hash: BlockHash,
timestamp: Timestamp,
era_id: EraId,
height: u64,
}
impl From<Block> for MinimalBlockInfo {
fn from(block: Block) -> Self {
MinimalBlockInfo {
hash: *block.hash(),
timestamp: block.header().timestamp(),
era_id: block.header().era_id(),
height: block.header().height(),
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct GetStatusResult {
pub api_version: Version,
pub chainspec_name: String,
pub genesis_root_hash: String,
pub peers: BTreeMap<String, SocketAddr>,
pub last_added_block_info: Option<MinimalBlockInfo>,
pub build_version: String,
}
impl From<StatusFeed<NodeId>> for GetStatusResult {
fn from(status_feed: StatusFeed<NodeId>) -> Self {
let chainspec_name = status_feed.chainspec_info.name();
let genesis_root_hash = status_feed
.chainspec_info
.root_hash()
.unwrap_or_default()
.to_string();
GetStatusResult {
api_version: CLIENT_API_VERSION.clone(),
chainspec_name,
genesis_root_hash,
peers: peers_hashmap_to_btreemap(status_feed.peers),
last_added_block_info: status_feed.last_added_block.map(Into::into),
build_version: crate::VERSION_STRING.clone(),
}
}
}
pub struct GetStatus {}
impl RpcWithoutParams for GetStatus {
const METHOD: &'static str = "info_get_status";
type ResponseResult = GetStatusResult;
}
impl RpcWithoutParamsExt for GetStatus {
fn handle_request<REv: ReactorEventT>(
effect_builder: EffectBuilder<REv>,
response_builder: Builder,
) -> BoxFuture<'static, Result<Response<Body>, Error>> {
async move {
let status_feed = effect_builder
.make_request(
|responder| ApiRequest::GetStatus { responder },
QueueKind::Api,
)
.await;
let result = Self::ResponseResult::from(status_feed);
Ok(response_builder.success(result)?)
}
.boxed()
}
}
fn peers_hashmap_to_btreemap(peers: HashMap<NodeId, SocketAddr>) -> BTreeMap<String, SocketAddr> {
peers
.into_iter()
.map(|(node_id, address)| (format!("{}", node_id), address))
.collect()
}