Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/gateway api #3970

Merged
merged 4 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
416 changes: 257 additions & 159 deletions Cargo.lock

Large diffs are not rendered by default.

16 changes: 13 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ members = [
"common/crypto",
"common/dkg",
"common/execute",
"common/http-requests",
"common/http-api-client",
"common/inclusion-probability",
"common/ledger",
"common/mixnode-common",
Expand Down Expand Up @@ -78,6 +78,7 @@ members = [
"common/wasm/storage",
"common/wasm/utils",
"common/wireguard",
"common/wireguard-types",
"explorer-api",
"explorer-api/explorer-api-requests",
"explorer-api/explorer-client",
Expand All @@ -93,6 +94,8 @@ members = [
"nym-api",
"nym-browser-extension/storage",
"nym-api/nym-api-requests",
"nym-node",
"nym-node/nym-node-requests",
"nym-outfox",
"tools/internal/ssl-inject",
"tools/internal/sdk-version-bump",
Expand Down Expand Up @@ -129,6 +132,8 @@ license = "Apache-2.0"
[workspace.dependencies]
anyhow = "1.0.71"
async-trait = "0.1.68"
axum = "0.6.20"
base64 = "0.21.4"
bip39 = { version = "2.0.0", features = ["zeroize"] }
boringtun = { git = "https://github.com/cloudflare/boringtun", rev = "e1d6360d6ab4529fc942a078e4c54df107abe2ba" }
cfg-if = "1.0.0"
Expand All @@ -152,22 +157,27 @@ dotenvy = "0.15.6"
futures = "0.3.28"
generic-array = "0.14.7"
getrandom = "0.2.10"
hyper = "0.14.27"
k256 = "0.13"
lazy_static = "1.4.0"
log = "0.4"
once_cell = "1.7.2"
parking_lot = "0.12.1"
rand = "0.8.5"
reqwest = "0.11.18"
reqwest = "0.11.22"
schemars = "0.8.1"
serde = "1.0.152"
serde_json = "1.0.91"
tap = "1.0.1"
tendermint-rpc = "0.32" # same version as used by cosmrs
thiserror = "1.0.38"
thiserror = "1.0.48"
tokio = "1.24.1"
tokio-tungstenite = "0.20.1"
tracing = "0.1.37"
tungstenite = { version = "0.20.1", default-features = false }
ts-rs = "7.0.0"
utoipa = "3.5.0"
utoipa-swagger-ui = "3.1.5"
url = "2.4"
zeroize = "1.6.0"

Expand Down
2 changes: 1 addition & 1 deletion common/bandwidth-controller/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ edition = "2021"
[dependencies]
bip39 = { workspace = true }
rand = "0.7.3"
thiserror = "1.0"
thiserror = { workspace = true }
url = { workspace = true }

nym-coconut-interface = { path = "../coconut-interface" }
Expand Down
4 changes: 4 additions & 0 deletions common/bin-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ clap_complete_fig = "4.0"
log = { workspace = true }
pretty_env_logger = "0.4.0"
semver = "0.11"
schemars = { workspace = true, features = ["preserve_order"], optional = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, optional = true }

Expand All @@ -29,6 +30,7 @@ opentelemetry-jaeger = { version = "0.18.0", optional = true, features = [
"isahc_collector_client",
] }
tracing-opentelemetry = { version = "0.19.0", optional = true }
utoipa = { workspace = true, optional = true }
opentelemetry = { version = "0.19.0", optional = true, features = ["rt-tokio"] }


Expand All @@ -42,7 +44,9 @@ vergen = { version = "=7.4.3", default-features = false, features = [

[features]
default = []
openapi = ["utoipa"]
output_format = ["serde_json"]
bin_info_schema = ["schemars"]
tracing = [
"tracing-subscriber",
"tracing-tree",
Expand Down
2 changes: 2 additions & 0 deletions common/bin-common/src/build_information/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ impl BinaryBuildInformation {
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "bin_info_schema", derive(schemars::JsonSchema))]
pub struct BinaryBuildInformationOwned {
/// Provides the name of the binary, i.e. the content of `CARGO_PKG_NAME` environmental variable.
pub binary_name: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ impl NymApiTopologyProvider {
Ok(mixes) => mixes,
};

let gateways = match self.validator_client.get_cached_gateways().await {
let gateways = match self.validator_client.get_cached_described_gateways().await {
Err(err) => {
error!("failed to get network gateways - {err}");
return None;
Expand Down
33 changes: 14 additions & 19 deletions common/client-core/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,29 +280,24 @@ impl GatewayEndpointConfig {
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)
}

pub fn from_node(node: nym_topology::gateway::Node, use_tls: bool) -> Self {
// TODO: in the future this shall return a Result and explicit `use_tls` will be removed in favour of the tls info being available on the struct
if use_tls {
Self::from_topology_node_tls(node)
pub fn from_node(
node: nym_topology::gateway::Node,
must_use_tls: bool,
) -> Result<Self, ClientCoreError> {
let gateway_listener = if must_use_tls {
node.clients_address_tls()
.ok_or(ClientCoreError::UnsupportedWssProtocol {
gateway: node.identity_key.to_base58_string(),
})?
} else {
Self::from_topology_node_no_tls(node)
}
}

pub fn from_topology_node_no_tls(node: nym_topology::gateway::Node) -> Self {
GatewayEndpointConfig {
gateway_id: node.identity_key.to_base58_string(),
gateway_listener: node.clients_address(),
gateway_owner: node.owner,
}
}
node.clients_address()
};

pub fn from_topology_node_tls(node: nym_topology::gateway::Node) -> Self {
GatewayEndpointConfig {
Ok(GatewayEndpointConfig {
gateway_id: node.identity_key.to_base58_string(),
gateway_listener: node.clients_address_tls(),
gateway_listener,
gateway_owner: node.owner,
}
})
}
}

Expand Down
6 changes: 6 additions & 0 deletions common/client-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ pub enum ClientCoreError {

#[error("this client has performed gateway initialisation in another session")]
NoInitClientPresent,

#[error("there are no gateways supporting the wss protocol available")]
NoWssGateways,

#[error("the specified gateway '{gateway}' does not support the wss protocol")]
UnsupportedWssProtocol { gateway: String },
}

/// Set of messages that the client can send to listeners via the task manager
Expand Down
44 changes: 38 additions & 6 deletions common/client-core/src/init/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ pub async fn current_gateways<R: Rng>(

log::trace!("Fetching list of gateways from: {nym_api}");

let gateways = client.get_cached_gateways().await?;
let gateways = client.get_cached_described_gateways().await?;
let valid_gateways = gateways
.into_iter()
.filter_map(|gateway| gateway.try_into().ok())
Expand Down Expand Up @@ -174,7 +174,10 @@ async fn measure_latency(gateway: &gateway::Node) -> Result<GatewayWithLatency,
pub async fn choose_gateway_by_latency<R: Rng>(
rng: &mut R,
gateways: &[gateway::Node],
must_use_tls: bool,
) -> Result<gateway::Node, ClientCoreError> {
let gateways = filter_by_tls(gateways, must_use_tls)?;

info!(
"choosing gateway by latency, pinging {} gateways ...",
gateways.len()
Expand Down Expand Up @@ -210,28 +213,57 @@ pub async fn choose_gateway_by_latency<R: Rng>(
Ok(chosen.gateway.clone())
}

fn filter_by_tls(
gateways: &[gateway::Node],
must_use_tls: bool,
) -> Result<Vec<&gateway::Node>, ClientCoreError> {
if must_use_tls {
let filtered = gateways
.iter()
.filter(|g| g.clients_wss_port.is_some())
.collect::<Vec<_>>();

if filtered.is_empty() {
return Err(ClientCoreError::NoWssGateways);
}

Ok(filtered)
} else {
Ok(gateways.iter().collect())
}
}

pub(super) fn uniformly_random_gateway<R: Rng>(
rng: &mut R,
gateways: &[gateway::Node],
must_use_tls: bool,
) -> Result<gateway::Node, ClientCoreError> {
gateways
filter_by_tls(gateways, must_use_tls)?
.choose(rng)
.ok_or(ClientCoreError::NoGatewaysOnNetwork)
.cloned()
.map(|&r| r.clone())
}

pub(super) fn get_specified_gateway(
gateway_identity: IdentityKeyRef,
gateways: &[gateway::Node],
must_use_tls: bool,
) -> Result<gateway::Node, ClientCoreError> {
let user_gateway = identity::PublicKey::from_base58_string(gateway_identity)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;

gateways
let gateway = gateways
.iter()
.find(|gateway| gateway.identity_key == user_gateway)
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))
.cloned()
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))?;

if must_use_tls && gateway.clients_wss_port.is_none() {
return Err(ClientCoreError::UnsupportedWssProtocol {
gateway: gateway_identity.to_string(),
});
}

Ok(gateway.clone())
}

pub(super) async fn register_with_gateway(
Expand Down
13 changes: 7 additions & 6 deletions common/client-core/src/init/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,19 +108,20 @@ where

let gateway_details = match selection_specification {
GatewaySelectionSpecification::UniformRemote { must_use_tls } => {
let gateway = uniformly_random_gateway(&mut rng, &available_gateways)?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
let gateway = uniformly_random_gateway(&mut rng, &available_gateways, must_use_tls)?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls)?)
}
GatewaySelectionSpecification::RemoteByLatency { must_use_tls } => {
let gateway = choose_gateway_by_latency(&mut rng, &available_gateways).await?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
let gateway =
choose_gateway_by_latency(&mut rng, &available_gateways, must_use_tls).await?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls)?)
}
GatewaySelectionSpecification::Specified {
must_use_tls,
identity,
} => {
let gateway = get_specified_gateway(&identity, &available_gateways)?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
let gateway = get_specified_gateway(&identity, &available_gateways, must_use_tls)?;
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls)?)
}
GatewaySelectionSpecification::Custom {
gateway_identity,
Expand Down
3 changes: 3 additions & 0 deletions common/client-libs/gateway-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ features = ["net", "sync", "time"]

[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
workspace = true
# the choice of this particular tls feature was arbitrary;
# if you reckon a different one would be more appropriate, feel free to change it
features = ["native-tls"]

# wasm-only dependencies
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
Expand Down
1 change: 1 addition & 0 deletions common/client-libs/validator-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ nym-service-provider-directory-common = { path = "../../cosmwasm-smart-contracts
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
reqwest = { workspace = true, features = ["json"] }
http-api-client = { path = "../../../common/http-api-client"}
thiserror = { workspace = true }
log = { workspace = true }
url = { workspace = true, features = ["serde"] }
Expand Down
19 changes: 13 additions & 6 deletions common/client-libs/validator-client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ use crate::{
use nym_api_requests::coconut::{
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
};
use nym_api_requests::models::MixNodeBondAnnotated;
use nym_api_requests::models::{DescribedGateway, MixNodeBondAnnotated};
use nym_api_requests::models::{
GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
RewardEstimationResponse, StakeSaturationResponse,
};
use nym_network_defaults::NymNetworkDetails;
use url::Url;

pub use crate::nym_api::NymApiClientExt;
pub use nym_mixnet_contract_common::{
mixnode::MixNodeDetails, GatewayBond, IdentityKey, IdentityKeyRef, MixId,
};
Expand Down Expand Up @@ -147,7 +148,7 @@ impl Client<ReqwestRpcClient> {

impl<C> Client<C> {
pub fn new_with_rpc_client(config: Config, rpc_client: C) -> Self {
let nym_api_client = nym_api::Client::new(config.api_url.clone());
let nym_api_client = nym_api::Client::new(config.api_url.clone(), None);

Client {
nym_api: nym_api_client,
Expand All @@ -161,7 +162,7 @@ impl<C, S> Client<C, S> {
where
S: OfflineSigner,
{
let nym_api_client = nym_api::Client::new(config.api_url.clone());
let nym_api_client = nym_api::Client::new(config.api_url.clone(), None);

Client {
nym_api: nym_api_client,
Expand All @@ -177,7 +178,7 @@ impl<C, S> Client<C, S> {
}

pub fn change_nym_api(&mut self, new_endpoint: Url) {
self.nym_api.change_url(new_endpoint)
self.nym_api.change_base_url(new_endpoint)
}

pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Expand Down Expand Up @@ -241,7 +242,7 @@ pub struct NymApiClient {

impl NymApiClient {
pub fn new(api_url: Url) -> Self {
let nym_api = nym_api::Client::new(api_url);
let nym_api = nym_api::Client::new(api_url, None);

NymApiClient { nym_api }
}
Expand All @@ -251,7 +252,7 @@ impl NymApiClient {
}

pub fn change_nym_api(&mut self, new_endpoint: Url) {
self.nym_api.change_url(new_endpoint);
self.nym_api.change_base_url(new_endpoint);
}

pub async fn get_cached_active_mixnodes(
Expand All @@ -274,6 +275,12 @@ impl NymApiClient {
Ok(self.nym_api.get_gateways().await?)
}

pub async fn get_cached_described_gateways(
&self,
) -> Result<Vec<DescribedGateway>, ValidatorClientError> {
Ok(self.nym_api.get_gateways_described().await?)
}

pub async fn get_gateway_core_status_count(
&self,
identity: IdentityKeyRef<'_>,
Expand Down
Loading