Skip to content

Commit

Permalink
feat(api): additional raw endpoints (#757)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
Co-authored-by: Alex Coats <[email protected]>
  • Loading branch information
3 people authored Oct 5, 2022
1 parent 12ec556 commit e27e38f
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 8 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ api = [
]
inx = [
"dep:bee-inx",
"dep:packable",
"dep:tonic",
]
opentelemetry = [
Expand Down
16 changes: 16 additions & 0 deletions src/bin/inx-chronicle/api/stardust/core/responses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,19 @@ pub struct InfoResponse {
}

impl_success_response!(InfoResponse);

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum OutputResponse {
Json(Box<bee_api_types_stardust::responses::OutputResponse>),
Raw(Vec<u8>),
}

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(),
}
}
}
50 changes: 42 additions & 8 deletions src/bin/inx-chronicle/api/stardust/core/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -258,25 +258,45 @@ fn create_output_metadata_response(
}
}

async fn output(database: Extension<MongoDb>, Path(output_id): Path<String>) -> ApiResult<OutputResponse> {
async fn output(
database: Extension<MongoDb>,
Path(output_id): Path<String>,
headers: HeaderMap,
) -> ApiResult<OutputResponse> {
let ledger_index = database
.collection::<MilestoneCollection>()
.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::<OutputCollection>()
.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::<ProtocolUpdateCollection>()
.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(
Expand All @@ -301,8 +321,22 @@ async fn output_metadata(
async fn transaction_included_block(
database: Extension<MongoDb>,
Path(transaction_id): Path<String>,
headers: HeaderMap,
) -> ApiResult<BlockResponse> {
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::<BlockCollection>()
.get_block_raw_for_transaction(&transaction_id)
.await?
.ok_or(ApiError::NoResults)?,
));
}
}

let block = database
.collection::<BlockCollection>()
.get_block_for_transaction(&transaction_id)
Expand Down
28 changes: 28 additions & 0 deletions src/db/collections/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Option<Vec<u8>>, Error> {
#[derive(Deserialize)]
struct RawResult {
#[serde(with = "serde_bytes")]
data: Vec<u8>,
}

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<Option<Block>, Error> {
self.aggregate(
Expand Down
7 changes: 7 additions & 0 deletions src/types/stardust/block/output/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -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)]
Expand Down Expand Up @@ -152,6 +154,11 @@ impl Output {
Self::Foundry(_) => true,
}
}

pub fn raw(self, ctx: ProtocolParameters) -> Result<Vec<u8>, 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<T: Borrow<bee::Output>> From<T> for Output {
Expand Down

0 comments on commit e27e38f

Please sign in to comment.