use async_trait::async_trait;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::time::Duration;
use std::time::SystemTime;
use std::{fmt, sync::Arc};
pub mod error;
pub mod types;
pub use error::Error;
pub mod utils;
pub use utils::now_millis;
pub mod blocks;
pub const ACCUMULATE_MAINNET_RPC_URL: &str = "https://mainnet.accumulatenetwork.io";
pub const ACCUMULATE_TESTNET_KERMIT_RPC_URL: &str = "https://api-gateway.accumulate.defidevs.io";
pub const ACCUMULATE_TESTNET_FOZZIE_RPC_URL: &str = "https://fozzie.accumulatenetwork.io";
pub const ACCUMULATE_EDGE_RPC_URL: &str = "https://api.accumulate.chainfold.io";
pub const ACCUMULATE_DEFAULT_RPC_URL: &str = "http://127.0.0.1:4467";
pub const ACCUMULATE_API_VER: u8 = 3;
pub const DEFAULT_TIMEOUT: u64 = 120;
pub const JSON_RPC: &str = "2.0";
pub type Result<T = ()> = std::result::Result<T, Error>;
#[derive(Clone, Debug)]
pub struct Client {
base_url: String,
version: u8,
client: reqwest::Client,
}
impl Default for Client {
fn default() -> Self {
Self::new_with_base_url(ACCUMULATE_DEFAULT_RPC_URL.to_string())
}
}
impl Client {
pub fn new() -> Self {
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(DEFAULT_TIMEOUT))
.build()
.unwrap();
Self {
base_url: ACCUMULATE_DEFAULT_RPC_URL.to_string(),
version: ACCUMULATE_API_VER,
client,
}
}
pub fn new_with_base_url(base_url: String) -> Self {
Self::new_with_timeout(base_url, ACCUMULATE_API_VER, DEFAULT_TIMEOUT)
}
pub fn new_with_timeout(base_url: String, version: u8, timeout: u64) -> Self {
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(timeout))
.build()
.unwrap();
Self {
base_url,
version,
client,
}
}
pub fn new_with_version(base_url: String, version: u8) -> Self {
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(DEFAULT_TIMEOUT))
.build()
.unwrap();
Self {
base_url,
version,
client,
}
}
async fn post<T: DeserializeOwned, D: Serialize>(&self, path: &str, data: D) -> Result<T> {
#[derive(Clone, Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub(crate) enum Response<T> {
Data { result: T, id: String },
Error { id: String, error: Error },
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub(crate) struct Error {
message: String,
code: isize,
}
let request_url = format!("{}{}", self.base_url, path);
println!("1// request URL: {}", request_url);
let request = self.client.post(&request_url).json(&data);
let requestV = self.client.post(&request_url).json(&data);
println!("2//");
match requestV.build() {
Ok(req) => {
println!("{:?}", req);
}
Err(e) => {
eprintln!("Error building the request: {:?}", e);
}
}
let response = request.send().await?;
println!("3//");
let body = response.text().await?;
println!("body: {}", body);
let v: Response<T> = serde_json::from_str(&body)?;
match v {
Response::Data { result, .. } => Ok(result),
Response::Error { error, .. } => Err(error::Error::ApiError(error.message, error.code)),
}
}
}
#[derive(Clone, Deserialize, Debug, Serialize)]
#[serde(tag = "method")]
enum Method {
#[serde(rename(serialize = "metrics"))]
Metrics,
#[serde(rename(serialize = "consensus-status"))]
ConsensusStatus,
#[serde(rename(serialize = "find-service"))]
FindService,
#[serde(rename(serialize = "query"))]
MajorBlocksGet { params: QueryBlockMajorListParams },
#[serde(rename(serialize = "query"))]
MinorBlocksGet { params: QueryBlockMinorListParams },
#[serde(rename(serialize = "query"))]
MajorBlockGet { params: BlockParams },
#[serde(rename(serialize = "query"))]
MinorBlockGet { params: BlockParams },
#[serde(rename(serialize = "query"))]
Query { params: QueryParams },
}
#[derive(Clone, Deserialize, Debug, Serialize)]
pub(crate) struct ApiCall {
jsonrpc: String,
id: String,
#[serde(flatten)]
method: Method,
}
impl ApiCall {
fn new(request: Method) -> ApiCall {
ApiCall {
jsonrpc: JSON_RPC.to_string(),
id: "0".to_string(), method: request,
}
}
pub(crate) fn query_major_blocks(url: String, start: u64, count: u64, expand: bool, from_end: bool) -> Self {
Self::new(Method::MajorBlocksGet {
params: QueryBlockMajorListParams {
scope: url,
query: {
BlockMajorListParams {
query_type: "block".to_string(),
major_range: BlockListRangeParams {
start,
count,
expand,
from_end,
},
}
},
},
})
}
pub(crate) fn query_major_block(url: String, height: u64) -> Self {
Self::new(Method::MajorBlockGet {
params: BlockParams { scope: url, height },
})
}
pub(crate) fn query_minor_blocks(url: String, start: u64, count: u64, expand: bool, from_end: bool) -> Self {
Self::new(Method::MinorBlocksGet {
params: QueryBlockMinorListParams {
scope: url,
query: {
BlockMinorListParams {
query_type: "block".to_string(),
major_range: BlockListRangeParams {
start,
count,
expand,
from_end,
},
}
},
},
})
}
}
#[derive(Clone, Deserialize, Debug, Serialize)]
struct QueryParams {
scope: String,
}
#[derive(Clone, Deserialize, Debug, Serialize)]
struct BlockParams {
scope: String,
height: u64,
}
#[derive(Clone, Deserialize, Debug, Serialize)]
struct QueryBlockMajorListParams {
scope: String,
query: BlockMajorListParams,
}
#[derive(Clone, Deserialize, Debug, Serialize)]
struct BlockMajorListParams {
#[serde(rename(serialize = "queryType"))]
query_type: String,
#[serde(rename(serialize = "majorRange"))]
major_range: BlockListRangeParams,
}
#[derive(Clone, Deserialize, Debug, Serialize)]
struct BlockListRangeParams {
start: u64,
count: u64,
expand: bool,
#[serde(rename(serialize = "fromEnd"))]
from_end: bool,
}
#[derive(Clone, Deserialize, Debug, Serialize)]
struct QueryBlockMinorListParams {
scope: String,
query: BlockMinorListParams,
}
#[derive(Clone, Deserialize, Debug, Serialize)]
struct BlockMinorListParams {
#[serde(rename(serialize = "queryType"))]
query_type: String,
#[serde(rename(serialize = "minorRange"))]
major_range: BlockListRangeParams,
}
#[cfg(test)]
mod test {
use super::*;
use tokio::test;
#[test]
async fn get_last_major_block() {
let client = Client::new_with_version(ACCUMULATE_MAINNET_RPC_URL.to_string(), 3);
let resp = blocks::get_range_major(&client, "acc://bvn-apollo.acme", 1, 100, true, true).await;
if let Err(err) = resp {
println!("{}", err);
panic!("{err}"); }
assert!(1 > 0);
}
}