nym-api: add service provider endpoint and caching

This commit is contained in:
Jon Häggblad
2023-04-19 16:11:35 +02:00
parent 2c4aee63bf
commit 9935c99d41
14 changed files with 103 additions and 17 deletions
Generated
+1
View File
@@ -3877,6 +3877,7 @@ name = "nym-service-provider-directory-common"
version = "0.1.0"
dependencies = [
"cosmwasm-std",
"schemars",
"serde",
]
@@ -15,6 +15,7 @@ use nym_api_requests::models::{
};
use nym_mixnet_contract_common::mixnode::MixNodeDetails;
use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixId};
use nym_service_provider_directory_common::ServiceInfo;
use reqwest::Response;
use serde::{Deserialize, Serialize};
use url::Url;
@@ -480,6 +481,11 @@ impl Client {
)
.await
}
pub async fn get_service_providers(&self) -> Result<Vec<ServiceInfo>, NymAPIError> {
self.query_nym_api(&[routes::API_VERSION, routes::SERVICE_PROVIDERS], NO_PARAMS)
.await
}
}
// utility function that should solve the double slash problem in validator API forever.
@@ -32,3 +32,5 @@ pub const COMPUTE_REWARD_ESTIMATION: &str = "compute-reward-estimation";
pub const AVG_UPTIME: &str = "avg_uptime";
pub const STAKE_SATURATION: &str = "stake-saturation";
pub const INCLUSION_CHANCE: &str = "inclusion-probability";
pub const SERVICE_PROVIDERS: &str = "service-providers";
@@ -2,7 +2,7 @@ use async_trait::async_trait;
use cosmrs::AccountId;
use nym_contracts_common::ContractBuildInformation;
use nym_service_provider_directory_common::{
msg::QueryMsg as SpQuery,
msg::QueryMsg as SpQueryMsg,
response::{
ConfigResponse, PagedServicesListResponse, ServiceInfoResponse, ServicesListResponse,
},
@@ -14,12 +14,12 @@ use crate::nyxd::{error::NyxdError, CosmWasmClient, NyxdClient};
#[async_trait]
pub trait SpDirectoryQueryClient {
async fn query_service_provider_contract<T>(&self, query: SpQuery) -> Result<T, NyxdError>
async fn query_service_provider_contract<T>(&self, query: SpQueryMsg) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>;
async fn get_service_config(&self) -> Result<ConfigResponse, NyxdError> {
self.query_service_provider_contract(SpQuery::Config {})
self.query_service_provider_contract(SpQueryMsg::Config {})
.await
}
@@ -27,7 +27,7 @@ pub trait SpDirectoryQueryClient {
&self,
service_id: ServiceId,
) -> Result<ServiceInfoResponse, NyxdError> {
self.query_service_provider_contract(SpQuery::ServiceId { service_id })
self.query_service_provider_contract(SpQueryMsg::ServiceId { service_id })
.await
}
@@ -36,7 +36,7 @@ pub trait SpDirectoryQueryClient {
start_after: Option<ServiceId>,
limit: Option<u32>,
) -> Result<PagedServicesListResponse, NyxdError> {
self.query_service_provider_contract(SpQuery::All { limit, start_after })
self.query_service_provider_contract(SpQueryMsg::All { limit, start_after })
.await
}
@@ -44,7 +44,7 @@ pub trait SpDirectoryQueryClient {
&self,
announcer: AccountId,
) -> Result<ServicesListResponse, NyxdError> {
self.query_service_provider_contract(SpQuery::ByAnnouncer {
self.query_service_provider_contract(SpQueryMsg::ByAnnouncer {
announcer: announcer.to_string(),
})
.await
@@ -54,12 +54,12 @@ pub trait SpDirectoryQueryClient {
&self,
nym_address: NymAddress,
) -> Result<ServicesListResponse, NyxdError> {
self.query_service_provider_contract(SpQuery::ByNymAddress { nym_address })
self.query_service_provider_contract(SpQueryMsg::ByNymAddress { nym_address })
.await
}
async fn get_sp_contract_version(&self) -> Result<ContractBuildInformation, NyxdError> {
self.query_service_provider_contract(SpQuery::GetContractVersion {})
self.query_service_provider_contract(SpQueryMsg::GetContractVersion {})
.await
}
@@ -89,7 +89,7 @@ impl<C> SpDirectoryQueryClient for NyxdClient<C>
where
C: CosmWasmClient + Send + Sync,
{
async fn query_service_provider_contract<T>(&self, query: SpQuery) -> Result<T, NyxdError>
async fn query_service_provider_contract<T>(&self, query: SpQueryMsg) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>,
{
@@ -105,3 +105,16 @@ where
.await
}
}
#[async_trait]
impl<C> SpDirectoryQueryClient for crate::Client<C>
where
C: CosmWasmClient + Send + Sync,
{
async fn query_service_provider_contract<T>(&self, query: SpQueryMsg) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>,
{
self.nyxd.query_service_provider_contract(query).await
}
}
@@ -7,4 +7,5 @@ edition = "2021"
[dependencies]
cosmwasm-std = { workspace = true }
schemars = "0.8"
serde = { workspace = true, default-features = false, features = ["derive"] }
@@ -1,5 +1,6 @@
use crate::{msg::ExecuteMsg, Service, ServiceId, ServiceInfo};
use cosmwasm_std::Coin;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
@@ -9,7 +10,7 @@ pub struct ServiceInfoResponse {
pub service: Option<Service>,
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct ServicesListResponse {
pub services: Vec<ServiceInfo>,
@@ -26,6 +27,14 @@ impl ServicesListResponse {
}
}
impl From<&[ServiceInfo]> for ServicesListResponse {
fn from(services: &[ServiceInfo]) -> Self {
Self {
services: services.to_vec(),
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct PagedServicesListResponse {
@@ -1,12 +1,13 @@
use std::fmt::{Display, Formatter};
use cosmwasm_std::{Addr, Coin};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
/// The directory of services are indexed by [`ServiceId`].
pub type ServiceId = u32;
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, JsonSchema)]
pub struct Service {
/// The address of the service.
pub nym_address: NymAddress,
@@ -21,7 +22,7 @@ pub struct Service {
}
/// The types of addresses supported.
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum NymAddress {
/// String representation of a nym address, which is of the form
@@ -51,7 +52,7 @@ impl Display for NymAddress {
}
/// The type of services provider supported
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Debug)]
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Debug, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ServiceType {
NetworkRequester,
@@ -66,7 +67,7 @@ impl std::fmt::Display for ServiceType {
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct ServiceInfo {
pub service_id: ServiceId,
+1
View File
@@ -82,6 +82,7 @@ nym-mixnet-contract-common = { path = "../common/cosmwasm-smart-contracts/mixnet
nym-vesting-contract-common = { path = "../common/cosmwasm-smart-contracts/vesting-contract" }
nym-contracts-common = { path = "../common/cosmwasm-smart-contracts/contracts-common" }
nym-multisig-contract-common = { path = "../common/cosmwasm-smart-contracts/multisig-contract" }
nym-service-provider-directory-common = { path = "../common/cosmwasm-smart-contracts/service-provider-directory" }
nym-coconut = { path = "../common/nymcoconut" }
nym-sphinx = { path = "../common/nymsphinx" }
nym-pemstore = { path = "../common/pemstore" }
+4
View File
@@ -6,6 +6,7 @@ use nym_mixnet_contract_common::{
families::FamilyHead, GatewayBond, IdentityKey, Interval, MixId, MixNodeDetails,
RewardingParams,
};
use nym_service_provider_directory_common::ServiceInfo;
use std::collections::HashSet;
pub(crate) struct ValidatorCacheData {
@@ -22,6 +23,8 @@ pub(crate) struct ValidatorCacheData {
pub(crate) current_interval: Cache<Option<Interval>>,
pub(crate) mix_to_family: Cache<Vec<(IdentityKey, FamilyHead)>>,
pub(crate) service_providers: Cache<Vec<ServiceInfo>>,
}
impl ValidatorCacheData {
@@ -36,6 +39,7 @@ impl ValidatorCacheData {
current_interval: Cache::default(),
current_reward_params: Cache::default(),
mix_to_family: Cache::default(),
service_providers: Cache::default(),
}
}
}
+14 -1
View File
@@ -5,6 +5,7 @@ use nym_mixnet_contract_common::{
families::FamilyHead, GatewayBond, IdentityKey, Interval, MixId, MixNodeBond, MixNodeDetails,
RewardingParams,
};
use nym_service_provider_directory_common::ServiceInfo;
use rocket::fairing::AdHoc;
use std::{
collections::HashSet,
@@ -50,6 +51,7 @@ impl NymContractCache {
rewarding_params: RewardingParams,
current_interval: Interval,
mix_to_family: Vec<(IdentityKey, FamilyHead)>,
services: Option<Vec<ServiceInfo>>,
) {
match time::timeout(Duration::from_millis(100), self.inner.write()).await {
Ok(mut cache) => {
@@ -59,7 +61,8 @@ impl NymContractCache {
cache.active_set.update(active_set);
cache.current_reward_params.update(Some(rewarding_params));
cache.current_interval.update(Some(current_interval));
cache.mix_to_family.update(mix_to_family)
cache.mix_to_family.update(mix_to_family);
cache.service_providers.update(services.unwrap_or_default());
}
Err(err) => {
error!("{err}");
@@ -286,6 +289,16 @@ impl NymContractCache {
self.mixnode_details(mix_id).await.1
}
pub(crate) async fn services(&self) -> Cache<Vec<ServiceInfo>> {
match time::timeout(Duration::from_millis(100), self.inner.read()).await {
Ok(cache) => cache.service_providers.clone(),
Err(err) => {
error!("{err}");
Cache::new(Vec::new())
}
}
}
pub fn initialised(&self) -> bool {
self.initialised.load(Ordering::Relaxed)
}
+5
View File
@@ -4,6 +4,7 @@ use crate::support::caching::CacheNotification;
use anyhow::Result;
use nym_mixnet_contract_common::{MixId, MixNodeDetails, RewardedSetNodeStatus};
use nym_task::TaskClient;
use nym_validator_client::nyxd::traits::SpDirectoryQueryClient;
use std::{collections::HashMap, sync::atomic::Ordering, time::Duration};
use tokio::sync::watch;
use tokio::time;
@@ -50,6 +51,9 @@ impl NymContractCacheRefresher {
let (rewarded_set, active_set) =
Self::collect_rewarded_and_active_set_details(&mixnodes, &rewarded_set_map);
// The service providers are optional
let services = self.nyxd_client.get_all_services().await.ok();
info!(
"Updating validator cache. There are {} mixnodes and {} gateways",
mixnodes.len(),
@@ -65,6 +69,7 @@ impl NymContractCacheRefresher {
rewarding_params,
current_interval,
mix_to_family,
services,
)
.await;
+2 -1
View File
@@ -27,7 +27,8 @@ pub(crate) fn nym_contract_cache_routes(settings: &OpenApiSettings) -> (Vec<Rout
routes::get_blacklisted_mixnodes,
routes::get_blacklisted_gateways,
routes::get_interval_reward_params,
routes::get_current_epoch
routes::get_current_epoch,
routes::get_services
]
}
+8
View File
@@ -13,6 +13,7 @@ use nym_mixnet_contract_common::{
mixnode::MixNodeDetails, reward_params::RewardingParams, GatewayBond, Interval, MixId,
};
use nym_service_provider_directory_common::response::ServicesListResponse;
use rocket::{serde::json::Json, State};
use rocket_okapi::openapi;
use std::collections::HashSet;
@@ -125,3 +126,10 @@ pub async fn get_interval_reward_params(
pub async fn get_current_epoch(cache: &State<NymContractCache>) -> Json<Option<Interval>> {
Json(cache.current_interval().await.value)
}
#[openapi(tag = "contract-cache")]
#[get("/services")]
pub async fn get_services(cache: &State<NymContractCache>) -> Json<ServicesListResponse> {
let services = cache.services().await.value;
Json(services.as_slice().into())
}
+22 -1
View File
@@ -25,8 +25,11 @@ use nym_mixnet_contract_common::{
CurrentIntervalResponse, EpochStatus, ExecuteMsg, GatewayBond, IdentityKey, LayerAssignment,
MixId, RewardedSetNodeStatus,
};
use nym_service_provider_directory_common::msg::QueryMsg as SpQueryMsg;
use nym_validator_client::nyxd::error::NyxdError;
use nym_validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient};
use nym_validator_client::nyxd::traits::{
MixnetQueryClient, MixnetSigningClient, SpDirectoryQueryClient,
};
use nym_validator_client::nyxd::{
cosmwasm_client::types::ExecuteResult,
traits::{
@@ -497,3 +500,21 @@ impl DkgQueryClient for Client {
self.0.read().await.nyxd.query_dkg_contract(query).await
}
}
#[async_trait]
impl SpDirectoryQueryClient for Client {
async fn query_service_provider_contract<T>(
&self,
query: SpQueryMsg,
) -> std::result::Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>,
{
self.0
.read()
.await
.nyxd
.query_service_provider_contract(query)
.await
}
}