From 58fe8bac2338340bf04fc77d973e7b8576169673 Mon Sep 17 00:00:00 2001 From: Torkel Rogstad Date: Thu, 27 Feb 2025 15:48:21 +0100 Subject: [PATCH] multi: add endpoints for best mainchain and sidechain hashes (#42) * multi: add endpoints for best mainchain and sidechain hashes * human-readable serialization for block hashes * dev: fmt code * fixups --------- Co-authored-by: Ash Manning --- app/rpc_server.rs | 23 +++++++++++++++++++++++ cli/lib.rs | 12 ++++++++++++ lib/node/mod.rs | 15 +++++++++++++++ lib/types/hashes.rs | 2 +- rpc-api/lib.rs | 22 +++++++++++++++++++++- rpc-api/schema.rs | 23 +++++++++++++++++------ 6 files changed, 89 insertions(+), 8 deletions(-) diff --git a/app/rpc_server.rs b/app/rpc_server.rs index c5e2897..202e5b1 100644 --- a/app/rpc_server.rs +++ b/app/rpc_server.rs @@ -98,6 +98,29 @@ impl RpcServer for RpcServerImpl { Ok(Some(block)) } + async fn get_best_sidechain_block_hash( + &self, + ) -> RpcResult> { + self.app.node.try_get_tip().map_err(custom_err) + } + + async fn get_best_mainchain_block_hash( + &self, + ) -> RpcResult> { + let Some(sidechain_hash) = + self.app.node.try_get_tip().map_err(custom_err)? + else { + // No sidechain tip, so no best mainchain block hash. + return Ok(None); + }; + let block_hash = self + .app + .node + .get_best_main_verification(sidechain_hash) + .map_err(custom_err)?; + Ok(Some(block_hash)) + } + async fn get_bmm_inclusions( &self, block_hash: thunder::types::BlockHash, diff --git a/cli/lib.rs b/cli/lib.rs index e99a0a3..c75bf54 100644 --- a/cli/lib.rs +++ b/cli/lib.rs @@ -27,6 +27,10 @@ pub enum Command { FormatDepositAddress { address: Address }, /// Generate a mnemonic seed phrase GenerateMnemonic, + /// Get the best mainchain block hash + GetBestMainchainBlockHash, + /// Get the best sidechain block hash + GetBestSidechainBlockHash, /// Get the block with specified block hash, if it exists GetBlock { block_hash: thunder::types::BlockHash, @@ -137,6 +141,14 @@ where let block = rpc_client.get_block(block_hash).await?; serde_json::to_string_pretty(&block)? } + Command::GetBestMainchainBlockHash => { + let block_hash = rpc_client.get_best_mainchain_block_hash().await?; + serde_json::to_string_pretty(&block_hash)? + } + Command::GetBestSidechainBlockHash => { + let block_hash = rpc_client.get_best_sidechain_block_hash().await?; + serde_json::to_string_pretty(&block_hash)? + } Command::GetBmmInclusions { block_hash } => { let bmm_inclusions = rpc_client.get_bmm_inclusions(block_hash).await?; diff --git a/lib/node/mod.rs b/lib/node/mod.rs index 6298e15..b0eb05a 100644 --- a/lib/node/mod.rs +++ b/lib/node/mod.rs @@ -318,6 +318,12 @@ where Ok(utxos) } + pub fn try_get_tip(&self) -> Result, Error> { + let rotxn = self.env.read_txn().map_err(EnvError::from)?; + let tip = self.state.try_get_tip(&rotxn).map_err(DbError::from)?; + Ok(tip) + } + pub fn get_tip_accumulator(&self) -> Result { let rotxn = self.env.read_txn().map_err(EnvError::from)?; Ok(self.state.get_accumulator(&rotxn)?) @@ -398,6 +404,15 @@ where Ok(self.archive.get_body(&rotxn, block_hash)?) } + pub fn get_best_main_verification( + &self, + hash: BlockHash, + ) -> Result { + let rotxn = self.env.read_txn().map_err(EnvError::from)?; + let hash = self.archive.get_best_main_verification(&rotxn, hash)?; + Ok(hash) + } + pub fn get_bmm_inclusions( &self, block_hash: BlockHash, diff --git a/lib/types/hashes.rs b/lib/types/hashes.rs index 8fae3f5..bd0e69a 100644 --- a/lib/types/hashes.rs +++ b/lib/types/hashes.rs @@ -24,7 +24,7 @@ pub type Hash = [u8; BLAKE3_LENGTH]; PartialOrd, Serialize, )] -pub struct BlockHash(pub Hash); +pub struct BlockHash(#[serde(with = "serde_hexstr_human_readable")] pub Hash); impl From for BlockHash { fn from(other: Hash) -> Self { diff --git a/rpc-api/lib.rs b/rpc-api/lib.rs index fb4d591..249551e 100644 --- a/rpc-api/lib.rs +++ b/rpc-api/lib.rs @@ -32,7 +32,9 @@ pub trait Rpc { #[method(name = "connect_peer")] async fn connect_peer( &self, - #[open_api_method_arg(schema(PartialSchema = "schema::SocketAddr"))] + #[open_api_method_arg(schema( + PartialSchema = "thunder_schema::SocketAddr" + ))] addr: SocketAddr, ) -> RpcResult<()>; @@ -74,6 +76,24 @@ pub trait Rpc { block_hash: thunder::types::BlockHash, ) -> RpcResult>; + /// Get the best mainchain block hash known by Thunder + #[open_api_method(output_schema( + PartialSchema = "schema::Optional" + ))] + #[method(name = "get_best_mainchain_block_hash")] + async fn get_best_mainchain_block_hash( + &self, + ) -> RpcResult>; + + /// Get the best sidechain block hash known by Thunder + #[open_api_method(output_schema( + PartialSchema = "schema::Optional" + ))] + #[method(name = "get_best_sidechain_block_hash")] + async fn get_best_sidechain_block_hash( + &self, + ) -> RpcResult>; + /// Get a new address #[method(name = "get_new_address")] async fn get_new_address(&self) -> RpcResult
; diff --git a/rpc-api/schema.rs b/rpc-api/schema.rs index 510c169..86a8226 100644 --- a/rpc-api/schema.rs +++ b/rpc-api/schema.rs @@ -1,5 +1,7 @@ //! Schemas for OpenAPI +use std::marker::PhantomData; + use utoipa::{ openapi::{self, RefOr, Schema}, PartialSchema, ToSchema, @@ -29,11 +31,20 @@ impl PartialSchema for OpenApi { } } -pub struct SocketAddr; - -impl PartialSchema for SocketAddr { - fn schema() -> RefOr { - let obj = utoipa::openapi::Object::with_type(openapi::Type::String); - RefOr::T(Schema::Object(obj)) +/// Optional `T` +pub struct Optional(PhantomData); + +impl PartialSchema for Optional +where + T: PartialSchema, +{ + fn schema() -> openapi::RefOr { + openapi::schema::OneOf::builder() + .item( + openapi::schema::Object::builder() + .schema_type(openapi::schema::Type::Null), + ) + .item(T::schema()) + .into() } }