From 4fcfc303c694d8f1806a5d99ceabe567dcb0fad7 Mon Sep 17 00:00:00 2001 From: peg Date: Wed, 23 Oct 2024 10:59:17 +0200 Subject: [PATCH] Add endpoint giving public keys --- crates/threshold-signature-server/src/lib.rs | 3 +- .../src/node_info/api.rs | 26 ++++++++++++-- .../src/node_info/errors.rs | 34 +++++++++++++++++++ .../src/node_info/mod.rs | 1 + .../src/node_info/tests.rs | 25 +++++++++++++- 5 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 crates/threshold-signature-server/src/node_info/errors.rs diff --git a/crates/threshold-signature-server/src/lib.rs b/crates/threshold-signature-server/src/lib.rs index 7ea5b0734..ccfaf6e05 100644 --- a/crates/threshold-signature-server/src/lib.rs +++ b/crates/threshold-signature-server/src/lib.rs @@ -185,7 +185,7 @@ use crate::{ attestation::api::attest, health::api::healthz, launch::Configuration, - node_info::api::{hashes, version as get_version}, + node_info::api::{hashes, info, version as get_version}, r#unsafe::api::{delete, put, remove_keys, unsafe_get}, signing_client::{api::*, ListenerState}, user::api::*, @@ -217,6 +217,7 @@ pub fn app(app_state: AppState) -> Router { .route("/healthz", get(healthz)) .route("/version", get(get_version)) .route("/hashes", get(hashes)) + .route("/info", get(info)) .route("/ws", get(ws_handler)); // Unsafe routes are for testing purposes only diff --git a/crates/threshold-signature-server/src/node_info/api.rs b/crates/threshold-signature-server/src/node_info/api.rs index 23a0e1526..30b98b5b6 100644 --- a/crates/threshold-signature-server/src/node_info/api.rs +++ b/crates/threshold-signature-server/src/node_info/api.rs @@ -12,9 +12,13 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -use axum::Json; -use entropy_shared::types::HashingAlgorithm; +use crate::{get_signer_and_x25519_secret, node_info::errors::GetInfoError, AppState}; +use axum::{extract::State, Json}; +use entropy_shared::{types::HashingAlgorithm, X25519PublicKey}; +use serde::{Deserialize, Serialize}; +use sp_core::Pair; use strum::IntoEnumIterator; +use subxt::utils::AccountId32; /// Returns the version and commit data #[tracing::instrument] @@ -22,8 +26,26 @@ pub async fn version() -> String { format!("{}-{}", env!("CARGO_PKG_VERSION"), env!("VERGEN_GIT_DESCRIBE")) } +/// Lists the supported hashing algorithms #[tracing::instrument] pub async fn hashes() -> Json> { let hashing_algos = HashingAlgorithm::iter().collect::>(); Json(hashing_algos) } + +/// Public signing and encryption keys associated with a TS server +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +pub struct TssPublicKeys { + pub tss_account: AccountId32, + pub x25519_public_key: X25519PublicKey, +} + +/// Returns the TS server's public keys and HTTP endpoint +#[tracing::instrument(skip_all)] +pub async fn info(State(app_state): State) -> Result, GetInfoError> { + let (signer, x25519_secret) = get_signer_and_x25519_secret(&app_state.kv_store).await?; + let tss_account = AccountId32(signer.signer().public().0); + let x25519_public_key = x25519_dalek::PublicKey::from(&x25519_secret).as_bytes().clone(); + + Ok(Json(TssPublicKeys { x25519_public_key, tss_account })) +} diff --git a/crates/threshold-signature-server/src/node_info/errors.rs b/crates/threshold-signature-server/src/node_info/errors.rs new file mode 100644 index 000000000..9936634f9 --- /dev/null +++ b/crates/threshold-signature-server/src/node_info/errors.rs @@ -0,0 +1,34 @@ +// Copyright (C) 2023 Entropy Cryptography Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +use axum::{ + http::StatusCode, + response::{IntoResponse, Response}, +}; +use thiserror::Error; + +/// Errors for protocol execution +#[derive(Debug, Error)] +pub enum GetInfoError { + #[error("Could not get public keys: {0}")] + User(#[from] crate::user::errors::UserErr), +} + +impl IntoResponse for GetInfoError { + fn into_response(self) -> Response { + tracing::error!("{:?}", format!("{self}")); + let body = format!("{self}").into_bytes(); + (StatusCode::INTERNAL_SERVER_ERROR, body).into_response() + } +} diff --git a/crates/threshold-signature-server/src/node_info/mod.rs b/crates/threshold-signature-server/src/node_info/mod.rs index 5837e4d77..3dcdf1f61 100644 --- a/crates/threshold-signature-server/src/node_info/mod.rs +++ b/crates/threshold-signature-server/src/node_info/mod.rs @@ -15,6 +15,7 @@ //! Provides information about this instance of `entropy-tss` pub mod api; +mod errors; #[cfg(test)] mod tests; diff --git a/crates/threshold-signature-server/src/node_info/tests.rs b/crates/threshold-signature-server/src/node_info/tests.rs index ba851a702..2676ecb87 100644 --- a/crates/threshold-signature-server/src/node_info/tests.rs +++ b/crates/threshold-signature-server/src/node_info/tests.rs @@ -13,9 +13,13 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -use crate::helpers::tests::{initialize_test_logger, setup_client}; +use crate::{ + helpers::tests::{initialize_test_logger, setup_client}, + node_info::api::TssPublicKeys, +}; use entropy_kvdb::clean_tests; use entropy_shared::types::HashingAlgorithm; +use entropy_testing_utils::constants::{TSS_ACCOUNTS, X25519_PUBLIC_KEYS}; use serial_test::serial; #[tokio::test] @@ -55,3 +59,22 @@ async fn hashes_test() { ); clean_tests(); } + +#[tokio::test] +#[serial] +async fn info_test() { + clean_tests(); + initialize_test_logger().await; + setup_client().await; + let client = reqwest::Client::new(); + let response = client.get("http://127.0.0.1:3001/info").send().await.unwrap(); + let public_keys: TssPublicKeys = response.json().await.unwrap(); + assert_eq!( + public_keys, + TssPublicKeys { + tss_account: TSS_ACCOUNTS[0].clone(), + x25519_public_key: X25519_PUBLIC_KEYS[0], + } + ); + clean_tests(); +}