diff --git a/CHANGELOG.md b/CHANGELOG.md index 95388bab7..2119b8c99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,9 @@ At the moment this project **does not** adhere to - In [#1153](https://github.com/entropyxyz/entropy-core/pull/1153/) the program runtime was updated to accept multiple oracle inputs, this means any programs that were compiled and used need to be recompiled to the new runtime +- In [#1128](https://github.com/entropyxyz/entropy-core/pull/1128) mnemonics can no longer be passed + in to `entropy-tss` via the `--mnemonic` command line argument, a file, or an environment variable. + Instead they are randomly generated internally and can be retrieved with the `/info` HTTP route. - In [#1179](https://github.com/entropyxyz/entropy-core/pull/1179) the format of TDX Quote input data has been changed. - In [#1147](https://github.com/entropyxyz/entropy-core/pull/1147) a field is added to the @@ -31,6 +34,8 @@ runtime for testing. If this is not desired it should be set to `None`. ### Added +- In [#1128](https://github.com/entropyxyz/entropy-core/pull/1128) an `/info` route was added to `entropy-tss` + which can be used to get the TSS account ID and x25519 public key. - Protocol message versioning ([#1140](https://github.com/entropyxyz/entropy-core/pull/1140)) - CLI command to get oracle headings ([#1170](https://github.com/entropyxyz/entropy-core/pull/1170)) - Add TSS endpoint to get TDX quote ([#1173](https://github.com/entropyxyz/entropy-core/pull/1173)) diff --git a/crates/threshold-signature-server/src/helpers/launch.rs b/crates/threshold-signature-server/src/helpers/launch.rs index 444d98f62..260fbdb47 100644 --- a/crates/threshold-signature-server/src/helpers/launch.rs +++ b/crates/threshold-signature-server/src/helpers/launch.rs @@ -207,37 +207,6 @@ pub struct StartupArgs { /// Returns the AccountID and Diffie-Hellman Public Keys associated with this server. #[arg(long = "setup-only")] pub setup_only: bool, - - /// The BIP-39 mnemonic (i.e seed phrase) to use for deriving the Threshold Signature Server - /// SR25519 account ID and the X25519 public key. - /// - /// The SR25519 account is responsible for signing and submitting extrinsics to the Entropy - /// network. - /// - /// The X25519 public key is used for encrypting/decrypting messages to other threshold - /// servers. - /// - /// Note that this may keep a mnemonic in your shell history. If you would like to avoid this - /// use one of the alternative options, or tools like the 1Password CLI. - /// - /// **Alternatives**: There are two other ways to supply the mnemonic to the TSS: the - /// `--mnemonic-file` flag and the `THRESHOLD_SERVER_MNEMONIC` environment variable. - /// - /// **Warning**: Passing this flag will overwrite any existing mnemonic! If you would like to - /// use an existing mnemonic omit this flag when running the process. - #[arg(long = "mnemonic")] - pub mnemonic: Option, - - /// The path to a file containing the BIP-39 mnemonic (i.e seed phrase) to use for deriving the - /// Threshold Signature Server SR25519 account ID and the X25519 public key. - /// - /// **Alternatives**: There are two other ways to supply the mnemonic to the TSS: the - /// `--mnemonic` flag and the `THRESHOLD_SERVER_MNEMONIC` environment variable. - /// - /// **Warning**: Passing this flag will overwrite any existing mnemonic! If you would like to - /// use an existing mnemonic omit this flag when running the process. - #[arg(long = "mnemonic-file", conflicts_with = "mnemonic")] - pub mnemonic_file: Option, } pub async fn has_mnemonic(kv: &KvManager) -> bool { diff --git a/crates/threshold-signature-server/src/lib.rs b/crates/threshold-signature-server/src/lib.rs index 2468208b2..5cd01cb49 100644 --- a/crates/threshold-signature-server/src/lib.rs +++ b/crates/threshold-signature-server/src/lib.rs @@ -110,6 +110,11 @@ //! http://127.0.0.1:3001/user/sign_tx //! ``` //! +//! ### For the node operator +//! +//! [`/info`](crate::node_info::api::info()) - Get - get a Json object of type +//! [crate::node_info::api::TssPublicKeys] which contains the TSS account ID and x25519 public key. +//! //! ### For the blockchain node //! //! ### For other instances of the threshold server @@ -185,7 +190,7 @@ use crate::{ attestation::api::{attest, get_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::*, @@ -218,6 +223,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/main.rs b/crates/threshold-signature-server/src/main.rs index c9e520e7d..ec00329b0 100644 --- a/crates/threshold-signature-server/src/main.rs +++ b/crates/threshold-signature-server/src/main.rs @@ -20,8 +20,8 @@ use clap::Parser; use entropy_tss::{ app, launch::{ - development_mnemonic, load_kv_store, setup_latest_block_number, setup_mnemonic, setup_only, - Configuration, StartupArgs, ValidatorName, + development_mnemonic, has_mnemonic, load_kv_store, setup_latest_block_number, + setup_mnemonic, setup_only, Configuration, StartupArgs, ValidatorName, }, AppState, }; @@ -66,39 +66,14 @@ async fn main() { let app_state = AppState::new(configuration.clone(), kv_store.clone()); - // We consider the inputs in order of most to least explicit: CLI flag, supplied file, - // environment variable. - let user_mnemonic = args - .mnemonic - .or_else(|| { - args.mnemonic_file.map(|path| { - let file = std::fs::read(path).expect("Unable to read mnemonic file."); - let mnemonic = std::str::from_utf8(&file) - .expect("Unable to convert provided mnemonic to UTF-8 string.") - .trim(); - - bip39::Mnemonic::parse_normalized(mnemonic) - .expect("Unable to parse given mnemonic.") - }) - }) - .or_else(|| { - std::env::var("THRESHOLD_SERVER_MNEMONIC").ok().map(|mnemonic| { - bip39::Mnemonic::parse_normalized(&mnemonic) - .expect("Unable to parse given mnemonic.") - }) - }); - - if let Some(mnemonic) = user_mnemonic { - setup_mnemonic(&kv_store, mnemonic).await - } else if cfg!(test) || validator_name.is_some() { + if cfg!(test) || validator_name.is_some() { setup_mnemonic(&kv_store, development_mnemonic(&validator_name)).await - } else { - let has_mnemonic = entropy_tss::launch::has_mnemonic(&kv_store).await; - assert!( - has_mnemonic, - "No mnemonic provided. Please provide one or use a development account." - ); - }; + } else if !has_mnemonic(&kv_store).await { + let mut rng = rand::thread_rng(); + let mnemonic = bip39::Mnemonic::generate_in_with(&mut rng, bip39::Language::English, 24) + .expect("Failed to generate mnemonic"); + setup_mnemonic(&kv_store, mnemonic).await + } setup_latest_block_number(&kv_store).await.expect("Issue setting up Latest Block Number"); diff --git a/crates/threshold-signature-server/src/node_info/api.rs b/crates/threshold-signature-server/src/node_info/api.rs index 23a0e1526..52aa40f73 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(); + + 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(); +}