diff --git a/docs/api_v2.md b/docs/api_v2.md index 682d535..456da76 100644 --- a/docs/api_v2.md +++ b/docs/api_v2.md @@ -12,7 +12,7 @@

1.1 根据区块号获取区块

-* `GET /api/v2/block/number` +* `GET /api/v2/number/block` * 参数 @@ -20,7 +20,7 @@ |-----------|----------|--------------|-----| | num | number | Y | 区块号 | -* Request: `http://localhost/api/v2/block/number?num=100` +* Request: `http://localhost/api/v2/number/block?num=100` * Response: ```json { @@ -67,7 +67,7 @@

1.2 根据区块哈希获取区块

-* `GET /api/v2/block/hash` +* `GET /api/v2/hash/block` * 参数 @@ -75,7 +75,7 @@ |-----------|----------|--------------|------| | hash | string | Y | 区块哈希 | -* Request: `http://localhost/api/v2/block/hash?hash=E8A4A1F0A6AE1EBAC0D8CA84106985DEFA47240A2AD4E045717CD304B8EDD985` +* Request: `http://localhost/api/v2/hash/block?hash=E8A4A1F0A6AE1EBAC0D8CA84106985DEFA47240A2AD4E045717CD304B8EDD985` * Response: ```json { diff --git a/explorer/src/config.toml b/explorer/src/config.toml index 039ec7c..8f5835e 100644 --- a/explorer/src/config.toml +++ b/explorer/src/config.toml @@ -4,7 +4,7 @@ [postgres] account = "postgres" - password = "csq2400306" + password = "12345678" addr = "localhost" database = "postgres" diff --git a/explorer/src/main.rs b/explorer/src/main.rs index 7798a18..12d635d 100644 --- a/explorer/src/main.rs +++ b/explorer/src/main.rs @@ -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; @@ -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 { @@ -29,10 +31,12 @@ async fn main() -> Result<()> { ); let pool: Pool = 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 }); @@ -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(); diff --git a/explorer/src/service/v2/block.rs b/explorer/src/service/v2/block.rs index 037e235..3377cdd 100644 --- a/explorer/src/service/v2/block.rs +++ b/explorer/src/service/v2/block.rs @@ -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, @@ -20,22 +19,23 @@ pub async fn get_block_by_num( State(state): State>, Query(params): Query, ) -> Result> { - 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, @@ -58,22 +58,25 @@ pub async fn get_block_by_hash( State(state): State>, Query(params): Query, ) -> Result> { - 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, @@ -97,31 +100,37 @@ pub async fn get_blocks( State(state): State>, Query(params): Query, ) -> Result>>> { - 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 = 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, diff --git a/explorer/src/service/v2/error.rs b/explorer/src/service/v2/error.rs index eccaa06..fb888eb 100644 --- a/explorer/src/service/v2/error.rs +++ b/explorer/src/service/v2/error.rs @@ -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 for ExplorerError { - fn from(e: serde_json::Error) -> Self { - ExplorerError::SerdeError(e) - } -} - -impl From for ExplorerError { - fn from(e: String) -> Self { - ExplorerError::Custom(e) - } -} - -impl From for ExplorerError { - fn from(e: url::ParseError) -> Self { - ExplorerError::ParseUrlError(e) - } -} - -impl From for ExplorerError { - fn from(e: rustc_hex::FromHexError) -> Self { - ExplorerError::HexError(e) - } -} - -impl From for ExplorerError { - fn from(e: std::io::Error) -> Self { - ExplorerError::IOError(e) - } -} - -impl From for ExplorerError { - fn from(e: toml::de::Error) -> Self { - ExplorerError::TomlDeError(e) - } -} - -impl From for ExplorerError { - fn from(e: sqlx::Error) -> Self { - ExplorerError::DBError(e) - } -} - -pub type Result = core::result::Result; - -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 = core::result::Result; - (StatusCode::INTERNAL_SERVER_ERROR, err_msg).into_response() - } +pub fn internal_error(err: E) -> (StatusCode, String) +where + E: std::error::Error, +{ + (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()) } diff --git a/explorer/src/service/v2/mod.rs b/explorer/src/service/v2/mod.rs index 79d63e0..d020a93 100644 --- a/explorer/src/service/v2/mod.rs +++ b/explorer/src/service/v2/mod.rs @@ -39,6 +39,7 @@ pub struct QueryResult { pub page_size: i32, pub data: T, } + #[derive(Serialize, Deserialize, Debug)] pub struct BlockResponse { pub block_hash: String, diff --git a/explorer/src/service/v2/transaction.rs b/explorer/src/service/v2/transaction.rs index 7715fe2..6f0bba8 100644 --- a/explorer/src/service/v2/transaction.rs +++ b/explorer/src/service/v2/transaction.rs @@ -1,118 +1,58 @@ -use crate::service::v1::transaction::{TxsData, TxsRes, TxsResponse}; -use crate::Api; -use anyhow::Result; -use log::debug; +use crate::service::v2::error::internal_error; +use crate::service::v2::error::Result; +use crate::AppState; +use axum::extract::{Query, State}; +use axum::Json; use module::schema::TransactionResponse; -use poem_openapi::param::Query; -use poem_openapi::payload::Json; +use serde::{Deserialize, Serialize}; use serde_json::Value; use sqlx::Row; -use std::ops::Add; -#[allow(dead_code)] -#[allow(clippy::too_many_arguments)] -pub async fn v2_get_txs( - api: &Api, - block_hash: Query>, - block_height: Query>, - from: Query>, - to: Query>, - ty: Query>, - start_time: Query>, - end_time: Query>, - page: Query>, - page_size: Query>, -) -> Result { - let mut conn = api.storage.lock().await.acquire().await?; - let mut sql_str = String::from("SELECT tx_hash,block_hash,ty_sub,timestamp,height,code,log,origin,result,value FROM transaction "); - let mut sql_total = String::from("SELECT count(*) as cnt FROM transaction "); - let mut params: Vec = vec![]; - if let Some(block_hash) = block_hash.0 { - params.push(format!("block_hash='{block_hash}' ")); - } - if let Some(height) = block_height.0 { - params.push(format!("height={height} ")); - } - if let Some(from_address) = from.0 { - params.push(format!("sender='{}' ", from_address)); - } - if let Some(to_address) = to.0 { - params.push(format!( - "(receiver @? '$.addrs[*] ? (@==\"{}\")') ", - to_address - )); - } - if let Some(ty) = ty.0 { - params.push(format!("ty={ty} ")); - } - if let Some(start_time) = start_time.0 { - params.push(format!("timestamp>={start_time} ")); - } - if let Some(end_time) = end_time.0 { - params.push(format!("timestamp<={end_time} ")); - } - let page = page.0.unwrap_or(1); - let page_size = page_size.0.unwrap_or(10); +use std::sync::Arc; - if !params.is_empty() { - sql_str = sql_str.add("WHERE ").add(params.join("AND ").as_str()); - sql_total = sql_total.add("WHERE ").add(params.join("AND ").as_str()); - } +#[derive(Serialize, Deserialize)] +pub struct GetTxByHashParams { + pub hash: String, +} - sql_str = sql_str.add( - format!( - "ORDER BY timestamp DESC LIMIT {} OFFSET {} ", - page_size, - (page - 1) * page_size - ) - .as_str(), - ); - debug!("{}", sql_str); - let rows = sqlx::query(sql_str.as_str()).fetch_all(&mut *conn).await?; - let mut txs: Vec = vec![]; +pub async fn get_tx_by_hash( + State(state): State>, + Query(params): Query, +) -> Result> { + let mut conn = state.pool.acquire().await.map_err(internal_error)?; - for row in rows { - let tx_hash: String = row.try_get("tx_hash")?; - let block_hash: String = row.try_get("block_hash")?; - let ty: i32 = row.try_get("ty_sub")?; - let timestamp: i64 = row.try_get("timestamp")?; - let height: i64 = row.try_get("height")?; - let code: i64 = row.try_get("code")?; - let log: String = "".to_string(); - let origin: String = row.try_get("origin")?; - let result: Value = row.try_get("result")?; - let value: Value = row.try_get("value")?; + let sql_query = r#"SELECT tx_hash,block_hash,height,timestamp,ty_sub,code,log,origin,result,value + FROM transaction WHERE tx_hash=$1"#; - let tx = TransactionResponse { - tx_hash, - evm_tx_hash: "".to_string(), - block_hash, - height, - timestamp, - code, - ty, - log, - origin, - result, - value, - }; + let row = sqlx::query(sql_query) + .bind(params.hash) + .fetch_one(&mut *conn) + .await + .map_err(internal_error)?; - txs.push(tx); - } + let tx_hash: String = row.try_get("tx_hash").map_err(internal_error)?; + let block_hash: String = row.try_get("block_hash").map_err(internal_error)?; + let ty: i32 = row.try_get("ty_sub").map_err(internal_error)?; + let timestamp: i64 = row.try_get("timestamp").map_err(internal_error)?; + let height: i64 = row.try_get("height").map_err(internal_error)?; + let code: i64 = row.try_get("code").map_err(internal_error)?; + let log: String = row.try_get("log").map_err(internal_error)?; + let origin: String = row.try_get("origin").map_err(internal_error)?; + let result: Value = row.try_get("result").map_err(internal_error)?; + let value: Value = row.try_get("value").map_err(internal_error)?; - // total items - let row_cnt = sqlx::query(sql_total.as_str()) - .fetch_one(&mut *conn) - .await?; - let total: i64 = row_cnt.try_get("cnt")?; + let tx = TransactionResponse { + tx_hash, + evm_tx_hash: "".to_string(), + block_hash, + height, + timestamp, + ty, + code, + log, + origin, + result, + value, + }; - Ok(TxsResponse::Ok(Json(TxsRes { - code: 200, - message: "".to_string(), - data: Some(TxsData { - page, - page_size, - total, - txs, - }), - }))) + Ok(Json(tx)) }