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

Generate mnemonic internally in entropy-tss #1128

Merged
merged 15 commits into from
Dec 18, 2024
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,18 @@ 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
chainspec: `jump_started_signers` which allows the chain to be started in a pre-jumpstarted state
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))
Expand Down
31 changes: 0 additions & 31 deletions crates/threshold-signature-server/src/helpers/launch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<bip39::Mnemonic>,

/// 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<PathBuf>,
}

pub async fn has_mnemonic(kv: &KvManager) -> bool {
Expand Down
8 changes: 7 additions & 1 deletion crates/threshold-signature-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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::*,
Expand Down Expand Up @@ -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
Expand Down
43 changes: 9 additions & 34 deletions crates/threshold-signature-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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");

Expand Down
26 changes: 24 additions & 2 deletions crates/threshold-signature-server/src/node_info/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,40 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
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]
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<Vec<HashingAlgorithm>> {
let hashing_algos = HashingAlgorithm::iter().collect::<Vec<_>>();
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<AppState>) -> Result<Json<TssPublicKeys>, 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 }))
}
34 changes: 34 additions & 0 deletions crates/threshold-signature-server/src/node_info/errors.rs
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.
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()
}
}
1 change: 1 addition & 0 deletions crates/threshold-signature-server/src/node_info/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

//! Provides information about this instance of `entropy-tss`
pub mod api;
mod errors;

#[cfg(test)]
mod tests;
25 changes: 24 additions & 1 deletion crates/threshold-signature-server/src/node_info/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

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]
Expand Down Expand Up @@ -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();
}
Loading