diff --git a/src/main.rs b/src/main.rs index 5592536..896dfb0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,7 +36,10 @@ use tx::types::{ BlockHeader, Hashable, ObjectIdentifier, ObjectMode, Transaction, TransactionInput, TransactionOpCode, TransactionOutpoint, TransactionOutput, }; -use types::{FaucetOutputPayload, HealthCheckResponse, ValueOutputEntry, ValueOutputsPayload}; +use types::{ + BlockDetailsPayload, BlockHeightPayload, FaucetOutputPayload, HealthCheckResponse, + ValueOutputEntry, ValueOutputsPayload, +}; use rpc::QuibleRpcServer; @@ -694,6 +697,77 @@ impl rpc::QuibleRpcServer for QuibleRpcServerImpl { )), } } + + async fn get_block_height(&self) -> Result { + let Some(block_height) = self + .db + .query("SELECT height FROM blocks ORDER BY height DESC LIMIT 1") + .await + .and_then(|mut response| response.take((0, "height"))) + .map_err(|err| { + ErrorObjectOwned::owned( + CALL_EXECUTION_FAILED_CODE, + "call execution failed: database query error", + Some(err.to_string()), + ) + })? + else { + return Err(ErrorObjectOwned::owned( + CALL_EXECUTION_FAILED_CODE, + "call execution failed: no blocks", + None as Option, + )); + }; + + Ok(BlockHeightPayload { + height: block_height, + }) + } + + async fn get_block_by_height( + &self, + height_payload: BlockHeightPayload, + ) -> Result { + let Some(block_row): Option = self + .db + .query("SELECT * FROM blocks WHERE height = $height LIMIT 1") + .bind(("height", height_payload.height)) + .await + .and_then(|mut response| response.take(0)) + .map_err(|err| { + ErrorObjectOwned::owned( + CALL_EXECUTION_FAILED_CODE, + "call execution failed: database query error", + Some(err.to_string()), + ) + })? + else { + return Err(ErrorObjectOwned::owned( + CALL_EXECUTION_FAILED_CODE, + "call execution failed: failed to find block", + None as Option, + )); + }; + + let mut block_hash = [0u8; 32]; + + hex::decode_to_slice(block_row.hash, &mut block_hash).map_err(|err| { + ErrorObjectOwned::owned( + CALL_EXECUTION_FAILED_CODE, + "call execution failed: failed to decode block hash", + Some(err.to_string()), + ) + })?; + + let block_details = BlockDetailsPayload { + hash: block_hash, + height: height_payload.height, + header: block_row.header, + transaction_count: block_row.transactions.len() as u64, + }; + + Ok(block_details) + } } async fn generate_intermediate_faucet_output( diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index 9a561c6..a5cc4ee 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -6,7 +6,9 @@ use jsonrpsee::types::ErrorObjectOwned; use crate::cert; use crate::tx::types::Transaction; -use crate::types::{self, FaucetOutputPayload, ValueOutputsPayload}; +use crate::types::{ + self, BlockDetailsPayload, BlockHeightPayload, FaucetOutputPayload, ValueOutputsPayload, +}; #[rpc(server, client, namespace = "quible")] pub trait QuibleRpc { @@ -37,4 +39,13 @@ pub trait QuibleRpc { #[method(name = "requestFaucetOutput")] async fn request_faucet_output(&self) -> Result; + + #[method(name = "getBlockHeight")] + async fn get_block_height(&self) -> Result; + + #[method(name = "getBlockByHeight")] + async fn get_block_by_height( + &self, + height_payload: BlockHeightPayload, + ) -> Result; } diff --git a/src/types.rs b/src/types.rs index 4d0134b..71289ff 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; +use serde_with::{serde_as, DisplayFromStr}; -use crate::tx::types::{Transaction, TransactionOutpoint}; +use crate::tx::types::{BlockHeader, Transaction, TransactionOutpoint}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SurrealID(pub surrealdb::sql::Thing); @@ -232,3 +233,21 @@ pub struct FaucetOutputPayload { pub value: u64, pub owner_signing_key: [u8; 32], } + +#[serde_as] +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BlockHeightPayload { + #[serde_as(as = "DisplayFromStr")] + pub height: u64, +} + +#[serde_as] +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BlockDetailsPayload { + pub hash: [u8; 32], + #[serde_as(as = "DisplayFromStr")] + pub height: u64, + pub header: BlockHeader, + #[serde_as(as = "DisplayFromStr")] + pub transaction_count: u64, +}