Skip to content

Commit

Permalink
Update SDK. Implement output full route and add some commented stub r…
Browse files Browse the repository at this point in the history
…outes. Add count to blocks by slot/commitment.
  • Loading branch information
Alex Coats committed Feb 28, 2024
1 parent 437fbd1 commit 5fc8c22
Show file tree
Hide file tree
Showing 10 changed files with 319 additions and 154 deletions.
212 changes: 95 additions & 117 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ humantime-serde = { version = "1.1", default-features = false }
iota-crypto = { version = "0.23", default-features = false, features = [ "blake2b", "ed25519", "slip10", "bip39-en", "random", "zeroize" ] }
iota-sdk = { git = "https://github.com/iotaledger/iota-sdk", branch = "2.0", default-features = false, features = [ "std", "serde" ] }
mongodb = { version = "2.4", default-features = false, features = [ "tokio-runtime" ] }
packable = { version = "0.10", default-features = false }
packable = { version = "0.11", default-features = false }
pin-project = { version = "1.0", default-features = false }
prefix-hex = { version = "0.7.0", default-features = false, features = [ "primitive-types", "std" ] }
primitive-types = { version = "0.12", default-features = false }
Expand Down
66 changes: 62 additions & 4 deletions src/bin/inx-chronicle/api/core/responses.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
// Copyright 2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use iota_sdk::types::{
api::core::{BaseTokenResponse, ProtocolParametersResponse},
block::slot::SlotCommitmentId,
use iota_sdk::{
types::{
api::core::{BaseTokenResponse, ProtocolParametersResponse},
block::{
address::Bech32Address,
output::{Output, OutputIdProof, OutputMetadata},
protocol::ProtocolParametersHash,
slot::{EpochIndex, SlotCommitmentId},
},
},
utils::serde::string,
};
use serde::{Deserialize, Serialize};

use crate::api::responses::impl_success_response;

/// Response of `GET /api/info`.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InfoResponse {
Expand All @@ -23,6 +30,57 @@ pub struct InfoResponse {

impl_success_response!(InfoResponse);

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FullOutputResponse {
pub output: Output,
pub output_id_proof: OutputIdProof,
pub metadata: OutputMetadata,
}

impl_success_response!(FullOutputResponse);

#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ValidatorResponse {
/// Account address of the validator.
pub address: Bech32Address,
/// The epoch index until which the validator registered to stake.
pub staking_end_epoch: EpochIndex,
/// The total stake of the pool, including delegators.
#[serde(with = "string")]
pub pool_stake: u64,
/// The stake of a validator.
#[serde(with = "string")]
pub validator_stake: u64,
/// The fixed cost of the validator, which it receives as part of its Mana rewards.
#[serde(with = "string")]
pub fixed_cost: u64,
/// Shows whether the validator was active recently.
pub active: bool,
/// The latest protocol version the validator supported.
pub latest_supported_protocol_version: u8,
/// The protocol hash of the latest supported protocol of the validator.
pub latest_supported_protocol_hash: ProtocolParametersHash,
}

impl_success_response!(ValidatorResponse);

#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ValidatorsResponse {
/// List of registered validators ready for the next epoch.
pub stakers: Vec<ValidatorResponse>,
/// The number of validators returned per one API request with pagination.
pub page_size: u32,
/// The cursor that needs to be provided as cursor query parameter to request the next page. If empty, this was the
/// last page.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cursor: Option<String>,
}

impl_success_response!(ValidatorsResponse);

/// A wrapper struct that allows us to implement [`IntoResponse`](axum::response::IntoResponse) for the foreign
/// responses from [`iota_sdk`](iota_sdk::types::api::core).
#[derive(Clone, Debug, Serialize, derive_more::From)]
Expand Down
113 changes: 110 additions & 3 deletions src/bin/inx-chronicle/api/core/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use iota_sdk::types::{
block::{
output::{
OutputConsumptionMetadata, OutputId, OutputInclusionMetadata, OutputMetadata as OutputMetadataResponse,
OutputWithMetadata,
},
payload::signed_transaction::TransactionId,
slot::{SlotCommitment, SlotCommitmentId, SlotIndex},
Expand Down Expand Up @@ -67,7 +68,7 @@ pub fn routes() -> Router<ApiState> {
Router::new()
.route("/:output_id", get(output))
.route("/:output_id/metadata", get(output_metadata))
.route("/:output_id/full", get(not_implemented)),
.route("/:output_id/full", get(output_full)),
)
.nest(
"/transactions",
Expand Down Expand Up @@ -240,14 +241,13 @@ async fn output(

async fn output_metadata(
database: State<MongoDb>,
Path(output_id): Path<String>,
Path(output_id): Path<OutputId>,
) -> ApiResult<IotaResponse<OutputMetadataResponse>> {
let latest_slot = database
.collection::<CommittedSlotCollection>()
.get_latest_committed_slot()
.await?
.ok_or(MissingError::NoResults)?;
let output_id = OutputId::from_str(&output_id).map_err(RequestError::from)?;
let metadata = database
.collection::<OutputCollection>()
.get_output_metadata(&output_id, latest_slot.slot_index)
Expand All @@ -257,6 +257,41 @@ async fn output_metadata(
Ok(create_output_metadata_response(metadata.output_id, metadata.metadata, latest_slot.commitment_id)?.into())
}

async fn output_full(
database: State<MongoDb>,
Path(output_id): Path<OutputId>,
) -> ApiResult<IotaResponse<OutputWithMetadata>> {
let latest_slot = database
.collection::<CommittedSlotCollection>()
.get_latest_committed_slot()
.await?
.ok_or(MissingError::NoResults)?;
let output_with_metadata = database
.collection::<OutputCollection>()
.get_output_with_metadata(&output_id, latest_slot.slot_index)
.await?
.ok_or(MissingError::NoResults)?;
let included_block = database
.collection::<BlockCollection>()
.get_block_for_transaction(output_id.transaction_id())
.await?
.ok_or(MissingError::NoResults)?;

Ok(OutputWithMetadata {
output: output_with_metadata.output,
output_id_proof: included_block
.block
.as_basic()
.payload()
.unwrap()
.as_signed_transaction()
.transaction()
.output_id_proof(output_id.index())?,
metadata: create_output_metadata_response(output_id, output_with_metadata.metadata, latest_slot.commitment_id)?,
}
.into())
}

async fn included_block(
database: State<MongoDb>,
Path(transaction_id): Path<TransactionId>,
Expand Down Expand Up @@ -359,3 +394,75 @@ async fn utxo_changes_by_index(
}
.into())
}

// async fn issuance(database: State<MongoDb>) -> ApiResult<IotaResponse<IssuanceBlockHeaderResponse>> {
// Ok(IssuanceBlockHeaderResponse {
// strong_parents: todo!(),
// weak_parents: todo!(),
// shallow_like_parents: todo!(),
// latest_parent_block_issuing_time: todo!(),
// latest_finalized_slot: todo!(),
// latest_commitment: todo!(),
// }
// .into())
// }

// async fn account_congestion(
// database: State<MongoDb>,
// Path(account_id): Path<AccountId>,
// ) -> ApiResult<IotaResponse<CongestionResponse>> {
// Ok(CongestionResponse {
// slot: todo!(),
// ready: todo!(),
// reference_mana_cost: todo!(),
// block_issuance_credits: todo!(),
// }
// .into())
// }

// async fn output_rewards(
// database: State<MongoDb>,
// Path(output_id): Path<OutputId>,
// ) -> ApiResult<IotaResponse<ManaRewardsResponse>> {
// Ok(ManaRewardsResponse {
// start_epoch: todo!(),
// end_epoch: todo!(),
// rewards: todo!(),
// latest_committed_epoch_pool_rewards: todo!(),
// }
// .into())
// }

// async fn all_validators(database: State<MongoDb>) -> ApiResult<ValidatorsResponse> {
// Ok(ValidatorsResponse {
// stakers: todo!(),
// page_size: todo!(),
// cursor: todo!(),
// })
// }

// async fn validator(database: State<MongoDb>, Path(account_id): Path<AccountId>) -> ApiResult<ValidatorResponse> {
// Ok(ValidatorResponse {
// address: todo!(),
// staking_end_epoch: todo!(),
// pool_stake: todo!(),
// validator_stake: todo!(),
// fixed_cost: todo!(),
// active: todo!(),
// latest_supported_protocol_version: todo!(),
// latest_supported_protocol_hash: todo!(),
// })
// }

// async fn committee(
// database: State<MongoDb>,
// Query(epochIndex): Query<EpochIndex>,
// ) -> ApiResult<IotaResponse<CommitteeResponse>> {
// Ok(CommitteeResponse {
// committee: todo!(),
// total_stake: todo!(),
// total_validator_stake: todo!(),
// epoch: todo!(),
// }
// .into())
// }
11 changes: 7 additions & 4 deletions src/bin/inx-chronicle/api/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use axum::{extract::rejection::QueryRejection, response::IntoResponse};
use axum_extra::typed_header::TypedHeaderRejection;
use chronicle::db::mongodb::collections::ParseSortError;
use hyper::{header::InvalidHeaderValue, StatusCode};
use iota_sdk::types::block::output::ProofError;
use serde::Serialize;
use thiserror::Error;
use tracing::error;
Expand Down Expand Up @@ -158,7 +159,6 @@ pub enum RequestError {
BadPagingState,
#[error("invalid time range")]
BadTimeRange,

#[error("invalid IOTA Stardust data: {0}")]
IotaStardust(#[from] iota_sdk::types::block::Error),
#[error("invalid bool value provided: {0}")]
Expand All @@ -173,9 +173,6 @@ pub enum RequestError {
InvalidAuthHeader(#[from] TypedHeaderRejection),
#[error("invalid query parameters provided: {0}")]
InvalidQueryParams(#[from] QueryRejection),
// #[cfg(feature = "poi")]
// #[error(transparent)]
// PoI(#[from] crate::api::poi::RequestError),
#[error("invalid sort order provided: {0}")]
SortOrder(#[from] ParseSortError),
}
Expand All @@ -200,6 +197,12 @@ pub enum ConfigError {
SecretKey(#[from] super::secret_key::SecretKeyError),
}

impl ErrorStatus for ProofError {
fn status(&self) -> StatusCode {
StatusCode::INTERNAL_SERVER_ERROR
}
}

#[derive(Clone, Debug, Serialize)]
pub struct ErrorBody {
#[serde(skip_serializing)]
Expand Down
1 change: 1 addition & 0 deletions src/bin/inx-chronicle/api/explorer/responses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ pub struct BlockPayloadTypeDto {
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BlocksBySlotResponse {
pub count: usize,
pub blocks: Vec<BlockPayloadTypeDto>,
pub cursor: Option<String>,
}
Expand Down
6 changes: 4 additions & 2 deletions src/bin/inx-chronicle/api/explorer/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,10 +259,12 @@ async fn blocks_by_slot_index(
cursor,
}: BlocksBySlotIndexPagination,
) -> ApiResult<BlocksBySlotResponse> {
let mut record_stream = database
let record_stream = database
.collection::<BlockCollection>()
.get_blocks_by_slot_index(index, page_size + 1, cursor, sort)
.await?;
let count = record_stream.count;
let mut record_stream = record_stream.stream;

// Take all of the requested records first
let blocks = record_stream
Expand All @@ -284,7 +286,7 @@ async fn blocks_by_slot_index(
.to_string()
});

Ok(BlocksBySlotResponse { blocks, cursor })
Ok(BlocksBySlotResponse { count, blocks, cursor })
}

async fn blocks_by_commitment_id(
Expand Down
Loading

0 comments on commit 5fc8c22

Please sign in to comment.