Skip to content

Commit

Permalink
Optimize: get tx by hash. (#248)
Browse files Browse the repository at this point in the history
  • Loading branch information
ch4ns1q1 authored Apr 7, 2024
1 parent b826aa2 commit 8bed25f
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 226 deletions.
8 changes: 4 additions & 4 deletions docs/api_v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@

<h3 id="1.1">1.1 根据区块号获取区块</h3>

* `GET /api/v2/block/number`
* `GET /api/v2/number/block`

* 参数

| 参数(param) | 类型(type) | 必传(required) | 说明 |
|-----------|----------|--------------|-----|
| num | number | Y | 区块号 |

* Request: `http://localhost/api/v2/block/number?num=100`
* Request: `http://localhost/api/v2/number/block?num=100`
* Response:
```json
{
Expand Down Expand Up @@ -67,15 +67,15 @@

<h3 id="1.2">1.2 根据区块哈希获取区块</h3>

* `GET /api/v2/block/hash`
* `GET /api/v2/hash/block`

* 参数

| 参数(param) | 类型(type) | 必传(required) | 说明 |
|-----------|----------|--------------|------|
| hash | string | Y | 区块哈希 |

* Request: `http://localhost/api/v2/block/hash?hash=E8A4A1F0A6AE1EBAC0D8CA84106985DEFA47240A2AD4E045717CD304B8EDD985`
* Request: `http://localhost/api/v2/hash/block?hash=E8A4A1F0A6AE1EBAC0D8CA84106985DEFA47240A2AD4E045717CD304B8EDD985`
* Response:
```json
{
Expand Down
2 changes: 1 addition & 1 deletion explorer/src/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

[postgres]
account = "postgres"
password = "csq2400306"
password = "12345678"
addr = "localhost"
database = "postgres"

Expand Down
13 changes: 9 additions & 4 deletions explorer/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod service;
use crate::service::api::Api;
use crate::service::v2::block::{get_block_by_hash, get_block_by_num, get_blocks};
use crate::service::v2::transaction::get_tx_by_hash;
use anyhow::Result;
use axum::http::Method;
use axum::routing::get;
Expand All @@ -9,6 +10,7 @@ use log::info;
use sqlx::pool::PoolOptions;
use sqlx::{PgPool, Pool, Postgres};
use std::sync::Arc;
use std::time::Duration;
use tower_http::cors::{Any, CorsLayer};

struct AppState {
Expand All @@ -29,10 +31,12 @@ async fn main() -> Result<()> {
);

let pool: Pool<Postgres> = PoolOptions::new()
.max_connections(50)
.max_connections(20)
.acquire_timeout(Duration::from_secs(5))
.connect(&postgres_config)
.await
.unwrap();
.expect("can't connect to database");

info!("Connecting DB...ok");

let app_state = Arc::new(AppState { pool });
Expand All @@ -42,9 +46,10 @@ async fn main() -> Result<()> {
.allow_headers(Any);
let addr = format!("{}:{}", config.server.addr, config.server.port);
let app = Router::new()
.route("/api/v2/block/number", get(get_block_by_num))
.route("/api/v2/block/hash", get(get_block_by_hash))
.route("/api/v2/number/block", get(get_block_by_num))
.route("/api/v2/hash/block", get(get_block_by_hash))
.route("/api/v2/blocks", get(get_blocks))
.route("/api/v2/hash/tx", get(get_tx_by_hash))
.layer(cors)
.with_state(app_state);
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
Expand Down
91 changes: 50 additions & 41 deletions explorer/src/service/v2/block.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
use crate::service::v2::error::internal_error;
use crate::service::v2::error::Result;
use crate::service::v2::{BlockResponse, QueryResult};
use crate::AppState;
use axum::extract::{Query, State};
use axum::Json;

use module::rpc::block::BlockRPC;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use sqlx::Row;
use std::sync::Arc;

use crate::service::v2::error::Result;
use crate::service::v2::{BlockResponse, QueryResult};

#[derive(Serialize, Deserialize, Debug)]
pub struct GetBlockByHeightParams {
pub num: i64,
Expand All @@ -20,22 +19,23 @@ pub async fn get_block_by_num(
State(state): State<Arc<AppState>>,
Query(params): Query<GetBlockByHeightParams>,
) -> Result<Json<BlockResponse>> {
let mut pool = state.pool.acquire().await?;
let mut pool = state.pool.acquire().await.map_err(internal_error)?;

let sql_query = "SELECT block_hash,height,size,tx_count,time,app_hash,proposer,block_data FROM block WHERE height=$1";
let sql_query = r#"SELECT block_hash,height,size,tx_count,time,app_hash,proposer,block_data FROM block WHERE height=$1"#;
let row = sqlx::query(sql_query)
.bind(params.num)
.fetch_one(&mut *pool)
.await?;

let block_hash: String = row.try_get("block_hash")?;
let block_num: i64 = row.try_get("height")?;
let app_hash: String = row.try_get("app_hash")?;
let proposer: String = row.try_get("proposer")?;
let block_size: i64 = row.try_get("size")?;
let num_txs: i64 = row.try_get("tx_count")?;
let block_data: Value = row.try_get("block_data")?;
let block_rpc: BlockRPC = serde_json::from_value(block_data)?;
.await
.map_err(internal_error)?;

let block_hash: String = row.try_get("block_hash").map_err(internal_error)?;
let block_num: i64 = row.try_get("height").map_err(internal_error)?;
let app_hash: String = row.try_get("app_hash").map_err(internal_error)?;
let proposer: String = row.try_get("proposer").map_err(internal_error)?;
let block_size: i64 = row.try_get("size").map_err(internal_error)?;
let num_txs: i64 = row.try_get("tx_count").map_err(internal_error)?;
let block_data: Value = row.try_get("block_data").map_err(internal_error)?;
let block_rpc: BlockRPC = serde_json::from_value(block_data).map_err(internal_error)?;

Ok(Json(BlockResponse {
block_hash,
Expand All @@ -58,22 +58,25 @@ pub async fn get_block_by_hash(
State(state): State<Arc<AppState>>,
Query(params): Query<GetBlockByHashParams>,
) -> Result<Json<BlockResponse>> {
let mut pool = state.pool.acquire().await?;
let mut pool = state.pool.acquire().await.map_err(internal_error)?;

let sql_query = r#"SELECT block_hash,height,size,tx_count,time,app_hash,proposer,block_data
FROM block WHERE block_hash=$1"#;

let sql_query = "SELECT block_hash,height,size,tx_count,time,app_hash,proposer,block_data FROM block WHERE block_hash=$1";
let row = sqlx::query(sql_query)
.bind(params.hash.to_uppercase())
.fetch_one(&mut *pool)
.await?;

let block_hash: String = row.try_get("block_hash")?;
let block_num: i64 = row.try_get("height")?;
let app_hash: String = row.try_get("app_hash")?;
let proposer: String = row.try_get("proposer")?;
let block_size: i64 = row.try_get("size")?;
let num_txs: i64 = row.try_get("tx_count")?;
let block_data: Value = row.try_get("block_data")?;
let block_rpc: BlockRPC = serde_json::from_value(block_data)?;
.await
.map_err(internal_error)?;

let block_hash: String = row.try_get("block_hash").map_err(internal_error)?;
let block_num: i64 = row.try_get("height").map_err(internal_error)?;
let app_hash: String = row.try_get("app_hash").map_err(internal_error)?;
let proposer: String = row.try_get("proposer").map_err(internal_error)?;
let block_size: i64 = row.try_get("size").map_err(internal_error)?;
let num_txs: i64 = row.try_get("tx_count").map_err(internal_error)?;
let block_data: Value = row.try_get("block_data").map_err(internal_error)?;
let block_rpc: BlockRPC = serde_json::from_value(block_data).map_err(internal_error)?;

Ok(Json(BlockResponse {
block_hash,
Expand All @@ -97,31 +100,37 @@ pub async fn get_blocks(
State(state): State<Arc<AppState>>,
Query(params): Query<GetBlocksParams>,
) -> Result<Json<QueryResult<Vec<BlockResponse>>>> {
let mut pool = state.pool.acquire().await?;
let mut pool = state.pool.acquire().await.map_err(internal_error)?;
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(10);

let sql_total = "SELECT max(height) FROM block";
let row = sqlx::query(sql_total).fetch_one(&mut *pool).await?;
let total = row.try_get("max")?;
let row = sqlx::query(sql_total)
.fetch_one(&mut *pool)
.await
.map_err(internal_error)?;
let total = row.try_get("max").map_err(internal_error)?;

let sql_query = r#"SELECT block_hash,height,size,tx_count,time,app_hash,proposer,block_data
FROM block ORDER BY height DESC LIMIT $1 OFFSET $2"#;

let sql_query = "SELECT block_hash,height,size,tx_count,time,app_hash,proposer,block_data FROM block ORDER BY height DESC LIMIT $1 OFFSET $2";
let rows = sqlx::query(sql_query)
.bind(page_size)
.bind((page - 1) * page_size)
.fetch_all(&mut *pool)
.await?;
.await
.map_err(internal_error)?;

let mut blocks: Vec<BlockResponse> = vec![];
for row in rows {
let block_hash: String = row.try_get("block_hash")?;
let block_num: i64 = row.try_get("height")?;
let app_hash: String = row.try_get("app_hash")?;
let proposer: String = row.try_get("proposer")?;
let block_size: i64 = row.try_get("size")?;
let num_txs: i64 = row.try_get("tx_count")?;
let block_data: Value = row.try_get("block_data")?;
let block_rpc: BlockRPC = serde_json::from_value(block_data)?;
let block_hash: String = row.try_get("block_hash").map_err(internal_error)?;
let block_num: i64 = row.try_get("height").map_err(internal_error)?;
let app_hash: String = row.try_get("app_hash").map_err(internal_error)?;
let proposer: String = row.try_get("proposer").map_err(internal_error)?;
let block_size: i64 = row.try_get("size").map_err(internal_error)?;
let num_txs: i64 = row.try_get("tx_count").map_err(internal_error)?;
let block_data: Value = row.try_get("block_data").map_err(internal_error)?;
let block_rpc: BlockRPC = serde_json::from_value(block_data).map_err(internal_error)?;

blocks.push(BlockResponse {
block_hash,
Expand Down
75 changes: 6 additions & 69 deletions explorer/src/service/v2/error.rs
Original file line number Diff line number Diff line change
@@ -1,73 +1,10 @@
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};

#[derive(Debug)]
pub enum ExplorerError {
Custom(String),
DBError(sqlx::Error),
IOError(std::io::Error),
TomlDeError(toml::de::Error),
HexError(rustc_hex::FromHexError),
ParseUrlError(url::ParseError),
SerdeError(serde_json::Error),
}

impl From<serde_json::Error> for ExplorerError {
fn from(e: serde_json::Error) -> Self {
ExplorerError::SerdeError(e)
}
}

impl From<String> for ExplorerError {
fn from(e: String) -> Self {
ExplorerError::Custom(e)
}
}

impl From<url::ParseError> for ExplorerError {
fn from(e: url::ParseError) -> Self {
ExplorerError::ParseUrlError(e)
}
}

impl From<rustc_hex::FromHexError> for ExplorerError {
fn from(e: rustc_hex::FromHexError) -> Self {
ExplorerError::HexError(e)
}
}

impl From<std::io::Error> for ExplorerError {
fn from(e: std::io::Error) -> Self {
ExplorerError::IOError(e)
}
}

impl From<toml::de::Error> for ExplorerError {
fn from(e: toml::de::Error) -> Self {
ExplorerError::TomlDeError(e)
}
}

impl From<sqlx::Error> for ExplorerError {
fn from(e: sqlx::Error) -> Self {
ExplorerError::DBError(e)
}
}

pub type Result<T> = core::result::Result<T, ExplorerError>;

impl IntoResponse for ExplorerError {
fn into_response(self) -> Response {
let err_msg = match self {
ExplorerError::Custom(e) => e,
ExplorerError::DBError(e) => e.to_string(),
ExplorerError::IOError(e) => e.to_string(),
ExplorerError::TomlDeError(e) => e.to_string(),
ExplorerError::HexError(e) => e.to_string(),
ExplorerError::ParseUrlError(e) => e.to_string(),
ExplorerError::SerdeError(e) => e.to_string(),
};
pub type Result<T> = core::result::Result<T, (StatusCode, String)>;

(StatusCode::INTERNAL_SERVER_ERROR, err_msg).into_response()
}
pub fn internal_error<E>(err: E) -> (StatusCode, String)
where
E: std::error::Error,
{
(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
}
1 change: 1 addition & 0 deletions explorer/src/service/v2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub struct QueryResult<T> {
pub page_size: i32,
pub data: T,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct BlockResponse {
pub block_hash: String,
Expand Down
Loading

0 comments on commit 8bed25f

Please sign in to comment.