From e27e38f5e6ba78d858f91882a5a016a39302b98b Mon Sep 17 00:00:00 2001 From: /alex/ Date: Wed, 5 Oct 2022 15:55:28 +0200 Subject: [PATCH] feat(api): additional raw endpoints (#757) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Extend endpoint to return raw block for included transaction * Add output raw endpoint * Add inx feature dependent on packable * Extend endpoint to return raw block for included transaction * Add output raw endpoint * Fetch protocol parameters for when the output was created * Manually impl IntoResponse for OutputResponse. * box the damn thing Co-authored-by: Jochen Görtler Co-authored-by: Alex Coats --- Cargo.toml | 1 + .../api/stardust/core/responses.rs | 16 ++++++ .../inx-chronicle/api/stardust/core/routes.rs | 50 ++++++++++++++++--- src/db/collections/block.rs | 28 +++++++++++ src/types/stardust/block/output/mod.rs | 7 +++ 5 files changed, 94 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1733b62ae..1b2c27e54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -114,6 +114,7 @@ api = [ ] inx = [ "dep:bee-inx", + "dep:packable", "dep:tonic", ] opentelemetry = [ diff --git a/src/bin/inx-chronicle/api/stardust/core/responses.rs b/src/bin/inx-chronicle/api/stardust/core/responses.rs index 1cad83a71..bd117e2d6 100644 --- a/src/bin/inx-chronicle/api/stardust/core/responses.rs +++ b/src/bin/inx-chronicle/api/stardust/core/responses.rs @@ -17,3 +17,19 @@ pub struct InfoResponse { } impl_success_response!(InfoResponse); + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum OutputResponse { + Json(Box), + Raw(Vec), +} + +impl axum::response::IntoResponse for OutputResponse { + fn into_response(self) -> axum::response::Response { + match self { + OutputResponse::Json(res) => axum::Json(res).into_response(), + OutputResponse::Raw(bytes) => bytes.into_response(), + } + } +} diff --git a/src/bin/inx-chronicle/api/stardust/core/routes.rs b/src/bin/inx-chronicle/api/stardust/core/routes.rs index b828886ba..fe0821936 100644 --- a/src/bin/inx-chronicle/api/stardust/core/routes.rs +++ b/src/bin/inx-chronicle/api/stardust/core/routes.rs @@ -13,8 +13,8 @@ use bee_api_types_stardust::{ dtos::ReceiptDto, responses::{ BlockMetadataResponse, BlockResponse, ConfirmedMilestoneResponse, LatestMilestoneResponse, MilestoneResponse, - OutputMetadataResponse, OutputResponse, ProtocolResponse, ReceiptsResponse, RentStructureResponse, - StatusResponse, TreasuryResponse, UtxoChangesResponse, + OutputMetadataResponse, ProtocolResponse, ReceiptsResponse, RentStructureResponse, StatusResponse, + TreasuryResponse, UtxoChangesResponse, }, }; use bee_block_stardust::payload::milestone::option::dto::MilestoneOptionDto; @@ -40,7 +40,7 @@ use futures::TryStreamExt; use lazy_static::lazy_static; use packable::PackableExt; -use super::responses::InfoResponse; +use super::responses::{InfoResponse, OutputResponse}; use crate::api::{ error::{ApiError, InternalApiError}, router::Router, @@ -258,25 +258,45 @@ fn create_output_metadata_response( } } -async fn output(database: Extension, Path(output_id): Path) -> ApiResult { +async fn output( + database: Extension, + Path(output_id): Path, + headers: HeaderMap, +) -> ApiResult { let ledger_index = database .collection::() .get_ledger_index() .await? .ok_or(ApiError::NoResults)?; let output_id = OutputId::from_str(&output_id).map_err(ApiError::bad_parse)?; + let OutputWithMetadataResult { output, metadata } = database .collection::() .get_output_with_metadata(&output_id, ledger_index) .await? .ok_or(ApiError::NoResults)?; + if let Some(value) = headers.get(axum::http::header::ACCEPT) { + if value.eq(&*BYTE_CONTENT_HEADER) { + let ctx = database + .collection::() + .get_protocol_parameters_for_ledger_index(metadata.booked.milestone_index) + .await? + .ok_or(ApiError::NoResults)? + .parameters; + + return Ok(OutputResponse::Raw(output.raw(ctx)?)); + } + } + let metadata = create_output_metadata_response(metadata, ledger_index); - Ok(OutputResponse { - metadata, - output: output.into(), - }) + Ok(OutputResponse::Json(Box::new( + bee_api_types_stardust::responses::OutputResponse { + metadata, + output: output.into(), + }, + ))) } async fn output_metadata( @@ -301,8 +321,22 @@ async fn output_metadata( async fn transaction_included_block( database: Extension, Path(transaction_id): Path, + headers: HeaderMap, ) -> ApiResult { let transaction_id = TransactionId::from_str(&transaction_id).map_err(ApiError::bad_parse)?; + + if let Some(value) = headers.get(axum::http::header::ACCEPT) { + if value.eq(&*BYTE_CONTENT_HEADER) { + return Ok(BlockResponse::Raw( + database + .collection::() + .get_block_raw_for_transaction(&transaction_id) + .await? + .ok_or(ApiError::NoResults)?, + )); + } + } + let block = database .collection::() .get_block_for_transaction(&transaction_id) diff --git a/src/db/collections/block.rs b/src/db/collections/block.rs index 78aabbb27..15f727ee3 100644 --- a/src/db/collections/block.rs +++ b/src/db/collections/block.rs @@ -249,6 +249,34 @@ impl BlockCollection { .await } + /// Finds the raw bytes of the block that included a transaction by [`TransactionId`]. + pub async fn get_block_raw_for_transaction( + &self, + transaction_id: &TransactionId, + ) -> Result>, Error> { + #[derive(Deserialize)] + struct RawResult { + #[serde(with = "serde_bytes")] + data: Vec, + } + + Ok(self + .aggregate( + vec![ + doc! { "$match": { + "metadata.inclusion_state": LedgerInclusionState::Included, + "block.payload.transaction_id": transaction_id, + } }, + doc! { "$replaceWith": { "data": "$raw" } }, + ], + None, + ) + .await? + .try_next() + .await? + .map(|RawResult { data }| data)) + } + /// Gets the spending transaction of an [`Output`](crate::types::stardust::block::Output) by [`OutputId`]. pub async fn get_spending_transaction(&self, output_id: &OutputId) -> Result, Error> { self.aggregate( diff --git a/src/types/stardust/block/output/mod.rs b/src/types/stardust/block/output/mod.rs index e7fa48af6..66f927246 100644 --- a/src/types/stardust/block/output/mod.rs +++ b/src/types/stardust/block/output/mod.rs @@ -16,6 +16,7 @@ use std::{borrow::Borrow, str::FromStr}; use bee_block_stardust::output as bee; use mongodb::bson::{doc, Bson}; +use packable::PackableExt; use serde::{Deserialize, Serialize}; pub use self::{ @@ -31,6 +32,7 @@ use super::Address; use crate::types::{ context::{TryFromWithContext, TryIntoWithContext}, stardust::block::payload::transaction::TransactionId, + tangle::ProtocolParameters, }; #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, derive_more::From)] @@ -152,6 +154,11 @@ impl Output { Self::Foundry(_) => true, } } + + pub fn raw(self, ctx: ProtocolParameters) -> Result, bee_block_stardust::Error> { + let bee_output = bee_block_stardust::output::Output::try_from_with_context(&ctx.try_into()?, self)?; + Ok(bee_output.pack_to_vec()) + } } impl> From for Output {