iota_rest_api/
committee.rs1use axum::extract::{Path, State};
6use iota_sdk2::types::{EpochId, ValidatorCommittee};
7use iota_types::storage::ReadStore;
8use tap::Pipe;
9
10use crate::{
11 RestService, Result,
12 accept::AcceptFormat,
13 openapi::{ApiEndpoint, OperationBuilder, ResponseBuilder, RouteHandler},
14 reader::StateReader,
15 response::ResponseContent,
16};
17
18pub struct GetLatestCommittee;
19
20impl ApiEndpoint<RestService> for GetLatestCommittee {
21 fn method(&self) -> axum::http::Method {
22 axum::http::Method::GET
23 }
24
25 fn path(&self) -> &'static str {
26 "/system/committee"
27 }
28
29 fn operation(
30 &self,
31 generator: &mut schemars::gen::SchemaGenerator,
32 ) -> openapiv3::v3_1::Operation {
33 OperationBuilder::new()
34 .tag("System")
35 .operation_id("GetLatestCommittee")
36 .response(
37 200,
38 ResponseBuilder::new()
39 .json_content::<ValidatorCommittee>(generator)
40 .bcs_content()
41 .build(),
42 )
43 .build()
44 }
45
46 fn handler(&self) -> RouteHandler<RestService> {
47 RouteHandler::new(self.method(), get_latest_committee)
48 }
49}
50
51async fn get_latest_committee(
52 accept: AcceptFormat,
53 State(state): State<StateReader>,
54) -> Result<ResponseContent<ValidatorCommittee>> {
55 let current_epoch = state.inner().get_latest_checkpoint()?.epoch();
56 let committee = state
57 .get_committee(current_epoch)?
58 .ok_or_else(|| CommitteeNotFoundError::new(current_epoch))?;
59
60 match accept {
61 AcceptFormat::Json => ResponseContent::Json(committee),
62 AcceptFormat::Bcs => ResponseContent::Bcs(committee),
63 }
64 .pipe(Ok)
65}
66
67pub struct GetCommittee;
68
69impl ApiEndpoint<RestService> for GetCommittee {
70 fn method(&self) -> axum::http::Method {
71 axum::http::Method::GET
72 }
73
74 fn path(&self) -> &'static str {
75 "/system/committee/{epoch}"
76 }
77
78 fn operation(
79 &self,
80 generator: &mut schemars::gen::SchemaGenerator,
81 ) -> openapiv3::v3_1::Operation {
82 OperationBuilder::new()
83 .tag("System")
84 .operation_id("GetCommittee")
85 .path_parameter::<EpochId>("epoch", generator)
86 .response(
87 200,
88 ResponseBuilder::new()
89 .json_content::<ValidatorCommittee>(generator)
90 .bcs_content()
91 .build(),
92 )
93 .response(404, ResponseBuilder::new().build())
94 .build()
95 }
96
97 fn handler(&self) -> RouteHandler<RestService> {
98 RouteHandler::new(self.method(), get_committee)
99 }
100}
101
102async fn get_committee(
103 Path(epoch): Path<EpochId>,
104 accept: AcceptFormat,
105 State(state): State<StateReader>,
106) -> Result<ResponseContent<ValidatorCommittee>> {
107 let committee = state
108 .get_committee(epoch)?
109 .ok_or_else(|| CommitteeNotFoundError::new(epoch))?;
110
111 match accept {
112 AcceptFormat::Json => ResponseContent::Json(committee),
113 AcceptFormat::Bcs => ResponseContent::Bcs(committee),
114 }
115 .pipe(Ok)
116}
117
118#[derive(Debug)]
119pub struct CommitteeNotFoundError {
120 epoch: EpochId,
121}
122
123impl CommitteeNotFoundError {
124 pub fn new(epoch: EpochId) -> Self {
125 Self { epoch }
126 }
127}
128
129impl std::fmt::Display for CommitteeNotFoundError {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 write!(f, "Committee for epoch {} not found", self.epoch)
132 }
133}
134
135impl std::error::Error for CommitteeNotFoundError {}
136
137impl From<CommitteeNotFoundError> for crate::RestError {
138 fn from(value: CommitteeNotFoundError) -> Self {
139 Self::new(axum::http::StatusCode::NOT_FOUND, value.to_string())
140 }
141}