Skip to content

Move NS client to separate package under NS API #5171

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

Merged
merged 6 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
36 changes: 17 additions & 19 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ members = [
"common/ip-packet-requests",
"common/ledger",
"common/mixnode-common",
"common/models",
"common/network-defaults",
"common/node-tester-utils",
"common/nonexhaustive-delayqueue",
Expand Down Expand Up @@ -127,6 +126,7 @@ members = [
"nym-node/nym-node-http-api",
"nym-node/nym-node-requests",
"nym-node-status-api",
"nym-node-status-api/client",
"nym-node-status-agent",
"nym-outfox",
"nym-validator-rewarder",
Expand Down Expand Up @@ -155,7 +155,6 @@ members = [
default-members = [
"clients/native",
"clients/socks5",
"common/models",
"explorer-api",
"gateway",
"mixnode",
Expand All @@ -164,6 +163,7 @@ default-members = [
"nym-data-observatory",
"nym-node",
"nym-node-status-api",
"nym-node-status-api/client",
"nym-validator-rewarder",
"nym-node-status-api",
"service-providers/authenticator",
Expand Down
17 changes: 0 additions & 17 deletions common/models/Cargo.toml

This file was deleted.

1 change: 0 additions & 1 deletion common/models/src/lib.rs

This file was deleted.

9 changes: 1 addition & 8 deletions nym-node-status-agent/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Copyright 2024 - Nym Technologies SA <[email protected]>
# SPDX-License-Identifier: Apache-2.0


[package]
name = "nym-node-status-agent"
version = "1.0.0-rc.1"
Expand All @@ -16,20 +15,14 @@ readme.workspace = true

[dependencies]
anyhow = { workspace = true}
bincode = { workspace = true }
chrono = { workspace = true }
clap = { workspace = true, features = ["derive", "env"] }
nym-bin-common = { path = "../common/bin-common", features = ["models"]}
nym-common-models = { path = "../common/models" }
nym-node-status-api-client = { path = "../nym-node-status-api/client" }
nym-crypto = { path = "../common/crypto", features = ["asymmetric", "rand"] }
rand = { workspace = true }
tokio = { workspace = true, features = ["macros", "rt-multi-thread", "process"] }
tokio-util = { workspace = true }
tracing = { workspace = true }
tracing-subscriber = { workspace = true, features = ["env-filter"] }
reqwest = { workspace = true, features = ["json"] }
serde = { workspace = true }
serde_json = { workspace = true }

[dev-dependencies]
tempfile = { workspace = true }
117 changes: 5 additions & 112 deletions nym-node-status-agent/src/cli/run_probe.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
use anyhow::{bail, Context};
use nym_common_models::ns_api::{get_testrun, submit_results, TestrunAssignment};
use nym_crypto::asymmetric::ed25519::{PrivateKey, Signature};
use std::fmt::Display;
use tracing::instrument;

use crate::cli::GwProbe;

const INTERNAL_TESTRUNS: &str = "internal/testruns";
use anyhow::Context;
use nym_crypto::asymmetric::ed25519::PrivateKey;

pub(crate) async fn run_probe(
server_ip: &str,
Expand All @@ -16,7 +10,9 @@ pub(crate) async fn run_probe(
) -> anyhow::Result<()> {
let auth_key = PrivateKey::from_base58_string(ns_api_auth_key)
.context("Couldn't parse auth key, exiting")?;
let ns_api_client = Client::new(server_ip, server_port, auth_key);

let ns_api_client =
nym_node_status_api_client::NsApiClient::new(server_ip, server_port, auth_key);

let probe = GwProbe::new(probe_path.to_string());

Expand All @@ -35,106 +31,3 @@ pub(crate) async fn run_probe(

Ok(())
}

struct Client {
server_address: String,
client: reqwest::Client,
auth_key: PrivateKey,
}

impl Client {
pub fn new(server_ip: &str, server_port: u16, auth_key: PrivateKey) -> Self {
let server_address = format!("{}:{}", server_ip, server_port);
let client = reqwest::Client::new();

Self {
server_address,
client,
auth_key,
}
}

#[instrument(level = "debug", skip_all)]
pub(crate) async fn request_testrun(&self) -> anyhow::Result<Option<TestrunAssignment>> {
let target_url = self.api_with_subpath(None::<String>);

let payload = get_testrun::Payload {
agent_public_key: self.auth_key.public_key(),
timestamp: chrono::offset::Utc::now().timestamp(),
};
let signature = self.sign_message(&payload)?;
let request = get_testrun::GetTestrunRequest { payload, signature };

let res = self.client.get(target_url).json(&request).send().await?;
let status = res.status();
let response_text = res.text().await?;

if status.is_client_error() {
bail!("{}: {}", status, response_text);
} else if status.is_server_error() {
if matches!(status, reqwest::StatusCode::SERVICE_UNAVAILABLE)
&& response_text.contains("No testruns available")
{
return Ok(None);
} else {
bail!("{}: {}", status, response_text);
}
}

serde_json::from_str(&response_text)
.map(|testrun| {
tracing::info!("Received testrun assignment: {:?}", testrun);
testrun
})
.map_err(|err| {
tracing::error!("err");
err.into()
})
}

#[instrument(level = "debug", skip(self, probe_result))]
pub(crate) async fn submit_results(
&self,
testrun_id: i64,
probe_result: String,
assigned_at_utc: i64,
) -> anyhow::Result<()> {
let target_url = self.api_with_subpath(Some(testrun_id));

let payload = submit_results::Payload {
probe_result,
agent_public_key: self.auth_key.public_key(),
assigned_at_utc,
};
let signature = self.sign_message(&payload)?;
let submit_results = submit_results::SubmitResults { payload, signature };

let res = self
.client
.post(target_url)
.json(&submit_results)
.send()
.await
.and_then(|response| response.error_for_status())?;

tracing::debug!("Submitted results: {})", res.status());
Ok(())
}

fn sign_message<T>(&self, message: &T) -> anyhow::Result<Signature>
where
T: serde::Serialize,
{
let serialized = bincode::serialize(message)?;
let signed = self.auth_key.sign(&serialized);
Ok(signed)
}

fn api_with_subpath(&self, subpath: Option<impl Display>) -> String {
if let Some(subpath) = subpath {
format!("{}/{}/{}", self.server_address, INTERNAL_TESTRUNS, subpath)
} else {
format!("{}/{}", self.server_address, INTERNAL_TESTRUNS)
}
}
}
3 changes: 1 addition & 2 deletions nym-node-status-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@ rust-version.workspace = true
[dependencies]
anyhow = { workspace = true }
axum = { workspace = true, features = ["tokio", "macros"] }
bincode = { workspace = true }
chrono = { workspace = true }
clap = { workspace = true, features = ["cargo", "derive", "env", "string"] }
cosmwasm-std = { workspace = true }
envy = { workspace = true }
futures-util = { workspace = true }
moka = { workspace = true, features = ["future"] }
nym-bin-common = { path = "../common/bin-common", features = ["models"]}
nym-common-models = { path = "../common/models" }
nym-node-status-api-client = { path = "client" }
nym-crypto = { path = "../common/crypto", features = ["asymmetric", "serde"] }
nym-explorer-client = { path = "../explorer-api/explorer-client" }
nym-network-defaults = { path = "../common/network-defaults" }
Expand Down
25 changes: 25 additions & 0 deletions nym-node-status-api/client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright 2024 - Nym Technologies SA <[email protected]>
# SPDX-License-Identifier: Apache-2.0

[package]
name = "nym-node-status-api-client"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
readme.workspace = true

[dependencies]
anyhow = { workspace = true }
chrono = { workspace = true }
bincode = { workspace = true }
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "serde"] }
nym-http-api-client = { path = "../../common/http-api-client" }
reqwest = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
tracing = { workspace = true }
18 changes: 18 additions & 0 deletions nym-node-status-api/client/src/api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use std::fmt::Display;

pub(super) struct ApiPaths {
server_address: String,
}

impl ApiPaths {
pub(super) fn new(server_address: String) -> Self {
Self { server_address }
}
pub(super) fn request_testrun(&self) -> String {
format!("{}/internal/testruns", self.server_address)
}

pub(super) fn submit_results(&self, testrun_id: impl Display) -> String {
format!("{}/internal/testruns/{}", self.server_address, testrun_id)
}
}
30 changes: 30 additions & 0 deletions nym-node-status-api/client/src/auth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use nym_crypto::asymmetric::ed25519::{PublicKey, Signature, SignatureError};

pub trait SignedRequest {
type Payload: serde::Serialize;

fn public_key(&self) -> &PublicKey;
fn signature(&self) -> &Signature;
fn payload(&self) -> &Self::Payload;
}

pub trait VerifiableRequest: SignedRequest {
type Error: From<bincode::Error> + From<SignatureError>;

fn verify_signature(&self) -> Result<(), Self::Error> {
bincode::serialize(self.payload())
.map_err(Self::Error::from)
.and_then(|serialized| {
self.public_key()
.verify(serialized, self.signature())
.map_err(Self::Error::from)
})
}
}

impl<T> VerifiableRequest for T
where
T: SignedRequest,
{
type Error = anyhow::Error;
}
Loading
Loading