diff --git a/catalyst-gateway/bin/src/service/api/v1/fragments_post.rs b/catalyst-gateway/bin/src/service/api/v1/fragments_post.rs new file mode 100644 index 00000000000..d16b35aa9e8 --- /dev/null +++ b/catalyst-gateway/bin/src/service/api/v1/fragments_post.rs @@ -0,0 +1,36 @@ +//! Implementation of the GET /fragments endpoint + +use poem_extensions::{response, UniResponse::T200}; +use poem_openapi::payload::Json; + +use crate::service::common::{ + objects::{ + fragments_batch::FragmentsBatch, fragments_processing_summary::FragmentsProcessingSummary, + }, + responses::{ + resp_2xx::OK, + resp_5xx::{ServerError, ServiceUnavailable}, + }, +}; + +/// All responses +pub(crate) type AllResponses = response! { + 200: OK>, + 500: ServerError, + 503: ServiceUnavailable, +}; + +/// # GET /fragments +/// +/// Process a fragments batch. +/// +/// ## Responses +/// +/// * 200 No Content - Service is OK and can keep running. +/// * 500 Server Error - If anything within this function fails unexpectedly. (Possible +/// but unlikely) +/// * 503 Service Unavailable - Service is possibly not running reliably. +#[allow(clippy::unused_async)] +pub(crate) async fn endpoint(_fragments_batch: FragmentsBatch) -> AllResponses { + T200(OK(Json(FragmentsProcessingSummary::default()))) +} diff --git a/catalyst-gateway/bin/src/service/api/v1/fragments_statuses.rs b/catalyst-gateway/bin/src/service/api/v1/fragments_statuses.rs new file mode 100644 index 00000000000..3ecfb42ba4a --- /dev/null +++ b/catalyst-gateway/bin/src/service/api/v1/fragments_statuses.rs @@ -0,0 +1,40 @@ +//! Implementation of the GET /fragments/statuses endpoint + +use std::collections::HashMap; + +use poem_extensions::{response, UniResponse::T200}; +use poem_openapi::payload::Json; + +use crate::service::common::{ + objects::fragment_status::FragmentStatus, + responses::{ + resp_2xx::OK, + resp_5xx::{ServerError, ServiceUnavailable}, + }, +}; + +/// Comma-separated (no spaces in between) list of fragment IDs. +#[derive(poem_openapi::NewType)] +pub(crate) struct FragmentIds(String); + +/// All responses +pub(crate) type AllResponses = response! { + 200: OK>>, + 500: ServerError, + 503: ServiceUnavailable, +}; + +/// # GET /fragments/statuses +/// +/// Get fragments statuses endpoint. +/// +/// ## Responses +/// +/// * 200 Fragments Statuses - Statuses of the fragments by id. +/// * 500 Server Error - If anything within this function fails unexpectedly. (Possible +/// but unlikely) +/// * 503 Service Unavailable - Service is possibly not running reliably. +#[allow(clippy::unused_async)] +pub(crate) async fn endpoint(_fragment_ids: FragmentIds) -> AllResponses { + T200(OK(Json(HashMap::new()))) +} diff --git a/catalyst-gateway/bin/src/service/api/v1/mod.rs b/catalyst-gateway/bin/src/service/api/v1/mod.rs index aaf7e875c5b..0b373aaa30f 100644 --- a/catalyst-gateway/bin/src/service/api/v1/mod.rs +++ b/catalyst-gateway/bin/src/service/api/v1/mod.rs @@ -3,14 +3,19 @@ use std::sync::Arc; use poem::web::{Data, Path}; -use poem_openapi::OpenApi; +use poem_openapi::{param::Query, payload::Json, OpenApi}; use crate::{ - service::common::{objects::account_votes::AccountId, tags::ApiTags}, + service::common::{ + objects::{account_votes::AccountId, fragments_batch::FragmentsBatch}, + tags::ApiTags, + }, state::State, }; mod account_votes_get; +mod fragments_post; +mod fragments_statuses; /// V1 API Endpoints pub(crate) struct V1Api; @@ -29,4 +34,41 @@ impl V1Api { ) -> account_votes_get::AllResponses { account_votes_get::endpoint(state, account_id).await } + + /// Process fragments + /// + /// Posts a fragments batch to be processed. + #[oai( + path = "/fragments", + method = "post", + operation_id = "fragments", + tag = "ApiTags::Fragments", + deprecated = true + )] + async fn fragments_post( + &self, + /// Batch of fragments to be processed. + Json(fragments_batch): Json, + ) -> fragments_post::AllResponses { + fragments_post::endpoint(fragments_batch).await + } + + /// Get Fragment Statuses + /// + /// Get statuses of the fragments with the given ids. + #[oai( + path = "/fragments/statuses", + method = "get", + operation_id = "fragmentsStatuses", + tag = "ApiTags::Fragments", + deprecated = true + )] + async fn fragments_statuses( + &self, + /// Comma-separated list of fragment ids for which the statuses will + /// be retrieved. + Query(fragment_ids): Query, + ) -> fragments_statuses::AllResponses { + fragments_statuses::endpoint(fragment_ids).await + } } diff --git a/catalyst-gateway/bin/src/service/common/objects/block.rs b/catalyst-gateway/bin/src/service/common/objects/block.rs new file mode 100644 index 00000000000..6ec83154e5a --- /dev/null +++ b/catalyst-gateway/bin/src/service/common/objects/block.rs @@ -0,0 +1,30 @@ +//! Defines API schemas of block related types. + +use poem_openapi::{types::Example, NewType, Object}; + +#[derive(NewType)] +/// Epoch number. +pub(crate) struct Epoch(pub u32); + +#[derive(NewType)] +/// Slot number. +pub(crate) struct Slot(pub u32); + +#[derive(Object)] +#[oai(example = true)] +/// Block time defined as the pair (epoch, slot). +pub(crate) struct BlockDate { + /// Block's epoch. + pub epoch: Epoch, + /// Block's slot number. + pub slot_id: Slot, +} + +impl Example for BlockDate { + fn example() -> Self { + Self { + epoch: Epoch(1), + slot_id: Slot(5), + } + } +} diff --git a/catalyst-gateway/bin/src/service/common/objects/fragment_status.rs b/catalyst-gateway/bin/src/service/common/objects/fragment_status.rs new file mode 100644 index 00000000000..814d480b18a --- /dev/null +++ b/catalyst-gateway/bin/src/service/common/objects/fragment_status.rs @@ -0,0 +1,57 @@ +//! Defines API schemas of fragment status types. + +use poem_openapi::{types::Example, Object, Union}; + +use crate::service::common::objects::{block::BlockDate, hash::Hash}; + +#[derive(Object)] +#[oai(example = false)] +/// DEPRECATED: Fragment is pending. +pub(crate) struct StatusPending; + +#[derive(Object)] +#[oai(example = true)] +/// DEPRECATED: Fragment was rejected. +pub(crate) struct StatusRejected { + /// Reason the fragment was rejected. + pub reason: String, +} + +impl Example for StatusRejected { + fn example() -> Self { + Self { + reason: "Transaction malformed".to_string(), + } + } +} + +#[derive(Object)] +#[oai(example = true)] +/// DEPRECATED: Fragment is included in a block. +pub(crate) struct StatusInABlock { + /// Block date at which the fragment was included in a block. + pub date: BlockDate, + /// Hash of the block the fragment was included in. + pub block: Hash, +} + +impl Example for StatusInABlock { + fn example() -> Self { + Self { + date: BlockDate::example(), + block: Hash::example(), + } + } +} + +#[derive(Union)] +#[oai(one_of = true)] +/// DEPRECATED: Possible fragment statuses. +pub(crate) enum FragmentStatus { + /// Fragment is pending inclusion in a block. + Pending(StatusPending), + /// Fragment was rejected. + Rejected(StatusRejected), + /// Fragment was included in a block. + InABlock(StatusInABlock), +} diff --git a/catalyst-gateway/bin/src/service/common/objects/fragments_batch.rs b/catalyst-gateway/bin/src/service/common/objects/fragments_batch.rs new file mode 100644 index 00000000000..fc1457f11fd --- /dev/null +++ b/catalyst-gateway/bin/src/service/common/objects/fragments_batch.rs @@ -0,0 +1,28 @@ +//! Defines API schemas for fragment batch types. + +use poem_openapi::{types::Example, NewType, Object}; +use serde::Deserialize; + +#[derive(NewType, Deserialize)] +/// Hex-encoded fragment's bytes. +pub(crate) struct FragmentDef(String); + +#[derive(Object, Deserialize)] +#[oai(example = true)] +/// Batch of hex-encoded fragments. +pub(crate) struct FragmentsBatch { + /// Fragments are processed sequentially. If this is true, processing is + /// stopped after the first error occurs. + pub fail_fast: bool, + /// Array of hex-encoded fragments bytes. + pub fragments: Vec, +} + +impl Example for FragmentsBatch { + fn example() -> Self { + Self { + fail_fast: false, + fragments: vec![], + } + } +} diff --git a/catalyst-gateway/bin/src/service/common/objects/hash.rs b/catalyst-gateway/bin/src/service/common/objects/hash.rs new file mode 100644 index 00000000000..55d99cf44d8 --- /dev/null +++ b/catalyst-gateway/bin/src/service/common/objects/hash.rs @@ -0,0 +1,20 @@ +//! Defines API schemas for hash types. + +use poem_openapi::{types::Example, Object}; + +#[derive(Object)] +#[oai(example = true)] +/// Blake2b256 hash wrapper. +pub(crate) struct Hash { + /// Blake2b256 hash encoded in hex. + #[oai(validator(max_length = 64, min_length = 64, pattern = "[0-9a-f]{64}"))] + pub hash: String, +} + +impl Example for Hash { + fn example() -> Self { + Self { + hash: "928b20366943e2afd11ebc0eae2e53a93bf177a4fcf35bcc64d503704e65e202".to_string(), + } + } +} diff --git a/catalyst-gateway/bin/src/service/common/objects/mod.rs b/catalyst-gateway/bin/src/service/common/objects/mod.rs index e014ae234df..7d358fef953 100644 --- a/catalyst-gateway/bin/src/service/common/objects/mod.rs +++ b/catalyst-gateway/bin/src/service/common/objects/mod.rs @@ -1,8 +1,12 @@ //! This module contains common and re-usable objects. pub(crate) mod account_votes; +pub(crate) mod block; pub(crate) mod delegate_public_key; pub(crate) mod event_id; +pub(crate) mod fragment_status; +pub(crate) mod fragments_batch; pub(crate) mod fragments_processing_summary; +pub(crate) mod hash; pub(crate) mod stake_public_key; pub(crate) mod vote_plan; pub(crate) mod voter_group_id; diff --git a/catalyst-gateway/bin/src/service/common/tags.rs b/catalyst-gateway/bin/src/service/common/tags.rs index b32add60e11..0e4f13398b8 100644 --- a/catalyst-gateway/bin/src/service/common/tags.rs +++ b/catalyst-gateway/bin/src/service/common/tags.rs @@ -4,6 +4,8 @@ use poem_openapi::Tags; /// `OpenAPI` Tags #[derive(Tags)] pub(crate) enum ApiTags { + /// Fragment endpoints + Fragments, /// Health Endpoints Health, /// Information relating to Voter Registration, Delegations and Calculated Voting