Skip to content

Commit

Permalink
fix(api): missing base token data in info response (#807)
Browse files Browse the repository at this point in the history
* Query node for base token information at startup

* Add node configuration collection

* Change message

* Clippy + Format

* Fix feature gates

* Nit

* Fix docs

* Fix merge

* Add test

* PR comments

* Improve test

* PR comments

* PR suggestions

* PR comments 2

* Fix test
  • Loading branch information
Alex6323 authored Oct 21, 2022
1 parent 2e0d4f4 commit a853b3e
Show file tree
Hide file tree
Showing 10 changed files with 353 additions and 15 deletions.
1 change: 1 addition & 0 deletions src/bin/inx-chronicle/api/stardust/core/responses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub struct InfoResponse {
pub version: String,
pub status: iota::StatusResponse,
pub protocol: iota::ProtocolResponse,
pub base_token: iota::BaseTokenResponse,
}

impl_success_response!(InfoResponse);
Expand Down
39 changes: 29 additions & 10 deletions src/bin/inx-chronicle/api/stardust/core/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ use axum::{
use chronicle::{
db::{
collections::{
BlockCollection, MilestoneCollection, OutputCollection, OutputMetadataResult, OutputWithMetadataResult,
ProtocolUpdateCollection, TreasuryCollection, UtxoChangesResult,
BlockCollection, ConfigurationUpdateCollection, MilestoneCollection, OutputCollection,
OutputMetadataResult, OutputWithMetadataResult, ProtocolUpdateCollection, TreasuryCollection,
UtxoChangesResult,
},
MongoDb,
},
Expand All @@ -32,9 +33,9 @@ use iota_types::{
api::{
dto::ReceiptDto,
response::{
self as iota, BlockMetadataResponse, ConfirmedMilestoneResponse, LatestMilestoneResponse,
OutputMetadataResponse, OutputResponse, ProtocolResponse, ReceiptsResponse, RentStructureResponse,
StatusResponse, TreasuryResponse, UtxoChangesResponse,
self as iota, BaseTokenResponse, BlockMetadataResponse, ConfirmedMilestoneResponse,
LatestMilestoneResponse, OutputMetadataResponse, OutputResponse, ProtocolResponse, ReceiptsResponse,
RentStructureResponse, StatusResponse, TreasuryResponse, UtxoChangesResponse,
},
},
block::{
Expand Down Expand Up @@ -158,9 +159,25 @@ pub async fn info(database: Extension<MongoDb>) -> ApiResult<InfoResponse> {
milestone_id: latest_milestone.milestone_id.clone(),
};

let base_token = database
.collection::<ConfigurationUpdateCollection>()
.get_latest_node_configuration()
.await?
.ok_or(ApiError::Internal(InternalApiError::CorruptState(
"no node configuration in the database",
)))?
.config
.base_token;

Ok(InfoResponse {
name: "Chronicle".into(),
version: std::env!("CARGO_PKG_VERSION").to_string(),
status: StatusResponse {
is_healthy,
latest_milestone,
confirmed_milestone,
pruning_index: oldest_milestone.milestone_index.0 - 1,
},
protocol: ProtocolResponse {
version: protocol.version,
network_name: protocol.network_name,
Expand All @@ -174,11 +191,13 @@ pub async fn info(database: Extension<MongoDb>) -> ApiResult<InfoResponse> {
},
token_supply: protocol.token_supply.to_string(),
},
status: StatusResponse {
is_healthy,
latest_milestone,
confirmed_milestone,
pruning_index: oldest_milestone.milestone_index.0 - 1,
base_token: BaseTokenResponse {
name: base_token.name,
ticker_symbol: base_token.ticker_symbol,
decimals: base_token.decimals as u8,
unit: base_token.unit,
subunit: Some(base_token.subunit),
use_metric_prefix: base_token.use_metric_prefix,
},
})
}
Expand Down
2 changes: 1 addition & 1 deletion src/bin/inx-chronicle/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

//! TODO
//! Module that holds the entry point of the Chronicle application.
/// Module containing the API.
#[cfg(feature = "api")]
Expand Down
35 changes: 32 additions & 3 deletions src/bin/inx-chronicle/stardust_inx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use std::{sync::Arc, time::Duration};
use chronicle::{
db::{
collections::{
Analytics, AnalyticsProcessor, BlockCollection, LedgerUpdateCollection, MilestoneCollection,
OutputCollection, ProtocolUpdateCollection, TreasuryCollection,
Analytics, AnalyticsProcessor, BlockCollection, ConfigurationUpdateCollection, LedgerUpdateCollection,
MilestoneCollection, OutputCollection, ProtocolUpdateCollection, TreasuryCollection,
},
InfluxDb, MongoDb,
},
Expand Down Expand Up @@ -148,7 +148,19 @@ impl InxWorker {
.params
.inner_unverified()?;

debug!("Connected to network `{}`.", protocol_parameters.network_name());
let node_configuration = inx.read_node_configuration().await?;

debug!(
"Connected to network `{}` with base token `{}[{}]`.",
protocol_parameters.network_name(),
node_configuration.base_token.name,
node_configuration.base_token.ticker_symbol
);

self.db
.collection::<ConfigurationUpdateCollection>()
.update_latest_node_configuration(node_status.ledger_index, node_configuration.into())
.await?;

if let Some(latest) = self
.db
Expand Down Expand Up @@ -333,6 +345,7 @@ impl InxWorker {

self.handle_cone_stream(inx, &analytics, milestone_index).await?;
self.handle_protocol_params(inx, milestone_index).await?;
self.handle_node_configuration(inx, milestone_index).await?;

// This acts as a checkpoint for the syncing and has to be done last, after everything else completed.
self.handle_milestone(
Expand Down Expand Up @@ -370,6 +383,22 @@ impl InxWorker {
Ok(())
}

#[instrument(skip_all, level = "trace")]
async fn handle_node_configuration(
&self,
inx: &mut Inx,
milestone_index: MilestoneIndex,
) -> Result<(), InxWorkerError> {
let node_configuration = inx.read_node_configuration().await?;

self.db
.collection::<ConfigurationUpdateCollection>()
.update_latest_node_configuration(milestone_index, node_configuration.into())
.await?;

Ok(())
}

#[instrument(skip_all, err, level = "trace")]
async fn handle_milestone(
&mut self,
Expand Down
104 changes: 104 additions & 0 deletions src/db/collections/configuration_update.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright 2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use mongodb::{bson::doc, error::Error, options::FindOneOptions};
use serde::{Deserialize, Serialize};

use crate::{
db::{
mongodb::{MongoDbCollection, MongoDbCollectionExt},
MongoDb,
},
types::{node::NodeConfiguration, tangle::MilestoneIndex},
};

/// The corresponding MongoDb document representation to store [`NodeConfiguration`]s.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct ConfigurationUpdateDocument {
#[serde(rename = "_id")]
pub ledger_index: MilestoneIndex,
#[serde(flatten)]
pub config: NodeConfiguration,
}

/// A collection to store [`NodeConfiguration`]s.
pub struct ConfigurationUpdateCollection {
collection: mongodb::Collection<ConfigurationUpdateDocument>,
}

impl MongoDbCollection for ConfigurationUpdateCollection {
const NAME: &'static str = "stardust_configuration_updates";
type Document = ConfigurationUpdateDocument;

fn instantiate(_db: &MongoDb, collection: mongodb::Collection<Self::Document>) -> Self {
Self { collection }
}

fn collection(&self) -> &mongodb::Collection<Self::Document> {
&self.collection
}
}

impl ConfigurationUpdateCollection {
/// Gets the latest node configuration.
pub async fn get_latest_node_configuration(&self) -> Result<Option<ConfigurationUpdateDocument>, Error> {
self.find_one(doc! {}, FindOneOptions::builder().sort(doc! { "_id": -1 }).build())
.await
}

/// Gets the node configuration that was valid for the given ledger index.
pub async fn get_node_configuration_for_ledger_index(
&self,
ledger_index: MilestoneIndex,
) -> Result<Option<ConfigurationUpdateDocument>, Error> {
self.find_one(
doc! { "_id": { "$lte": ledger_index } },
FindOneOptions::builder().sort(doc! { "_id": -1 }).build(),
)
.await
}

/// Inserts or updates a node configuration for a given ledger index.
pub async fn upsert_node_configuration(
&self,
ledger_index: MilestoneIndex,
config: NodeConfiguration,
) -> Result<(), Error> {
if let Some(latest_config) = self.get_node_configuration_for_ledger_index(ledger_index).await? {
if latest_config.ledger_index == ledger_index {
if latest_config.config != config {
self.replace_one(doc! {}, ConfigurationUpdateDocument { ledger_index, config }, None)
.await?;
}
} else {
self.insert_one(ConfigurationUpdateDocument { ledger_index, config }, None)
.await?;
}
} else {
self.insert_one(ConfigurationUpdateDocument { ledger_index, config }, None)
.await?;
}

Ok(())
}

/// Add the node configuration to this collection if it is newer and different.
pub async fn update_latest_node_configuration(
&self,
ledger_index: MilestoneIndex,
config: NodeConfiguration,
) -> Result<(), Error> {
if let Some(latest_config) = self.get_latest_node_configuration().await? {
if latest_config.ledger_index >= ledger_index {
return Ok(());
} else if latest_config.config != config {
self.insert_one(ConfigurationUpdateDocument { ledger_index, config }, None)
.await?;
}
} else {
self.insert_one(ConfigurationUpdateDocument { ledger_index, config }, None)
.await?;
}
Ok(())
}
}
3 changes: 3 additions & 0 deletions src/db/collections/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

/// Module containing the Block document model.
mod block;
/// Module containing the node configuration collection.
mod configuration_update;
/// Module containing the LedgerUpdate model.
mod ledger_update;
/// Module containing the Milestone document model.
Expand All @@ -23,6 +25,7 @@ use thiserror::Error;
pub use self::{
analytics::{Analytics, AnalyticsProcessor},
block::BlockCollection,
configuration_update::ConfigurationUpdateCollection,
ledger_update::{LedgerUpdateByAddressRecord, LedgerUpdateByMilestoneRecord, LedgerUpdateCollection},
milestone::{MilestoneCollection, MilestoneResult, SyncData},
outputs::{
Expand Down
21 changes: 20 additions & 1 deletion src/inx/node/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
use inx::proto;
use iota_types::block as iota;

use crate::{inx::InxError, maybe_missing};
use crate::{
inx::InxError,
maybe_missing,
types::node::{BaseToken, NodeConfiguration},
};

/// The [`BaseTokenMessage`] type.
#[derive(Clone, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -113,3 +117,18 @@ impl From<NodeConfigurationMessage> for proto::NodeConfiguration {
}
}
}

impl From<NodeConfigurationMessage> for NodeConfiguration {
fn from(value: NodeConfigurationMessage) -> Self {
Self {
base_token: BaseToken {
name: value.base_token.name,
ticker_symbol: value.base_token.ticker_symbol,
unit: value.base_token.unit,
subunit: value.base_token.subunit,
decimals: value.base_token.decimals,
use_metric_prefix: value.base_token.use_metric_prefix,
},
}
}
}
2 changes: 2 additions & 0 deletions src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ pub mod context;
#[cfg(feature = "stardust")]
pub mod ledger;
#[cfg(feature = "stardust")]
pub mod node;
#[cfg(feature = "stardust")]
pub mod stardust;
#[cfg(feature = "stardust")]
pub mod tangle;
Expand Down
25 changes: 25 additions & 0 deletions src/types/node/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

//! Module containing the node models.
use serde::{Deserialize, Serialize};

/// The [`NodeConfiguration`] type.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[allow(missing_docs)]
pub struct NodeConfiguration {
pub base_token: BaseToken,
}

/// The [`BaseToken`] type.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[allow(missing_docs)]
pub struct BaseToken {
pub name: String,
pub ticker_symbol: String,
pub unit: String,
pub subunit: String,
pub decimals: u32,
pub use_metric_prefix: bool,
}
Loading

0 comments on commit a853b3e

Please sign in to comment.