Feature/add wallet to gateway init (#984)
* Moving sign_text method into common/crypto to dry it up * Moved bech32 address validation into common/crypto * ibid * Gateway now requires a --wallet-address arg on init
This commit is contained in:
Generated
+2
-1
@@ -1092,6 +1092,7 @@ dependencies = [
|
||||
"blake3",
|
||||
"bs58",
|
||||
"cipher",
|
||||
"config",
|
||||
"digest 0.9.0",
|
||||
"ed25519-dalek",
|
||||
"generic-array 0.14.4",
|
||||
@@ -1101,6 +1102,7 @@ dependencies = [
|
||||
"nymsphinx-types",
|
||||
"pemstore",
|
||||
"rand 0.7.3",
|
||||
"subtle-encoding",
|
||||
"x25519-dalek",
|
||||
]
|
||||
|
||||
@@ -3694,7 +3696,6 @@ dependencies = [
|
||||
"rocket",
|
||||
"serde",
|
||||
"serial_test",
|
||||
"subtle-encoding",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"toml",
|
||||
|
||||
@@ -19,7 +19,9 @@ x25519-dalek = "1.1"
|
||||
ed25519-dalek = "1.0"
|
||||
log = "0.4"
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
subtle-encoding = { version = "0.5", features = ["bech32-preview"]}
|
||||
|
||||
# internal
|
||||
nymsphinx-types = { path = "../nymsphinx/types" }
|
||||
pemstore = { path = "../../common/pemstore" }
|
||||
config = { path="../../common/config" }
|
||||
|
||||
@@ -189,6 +189,13 @@ impl PrivateKey {
|
||||
let sig = expanded_secret_key.sign(message, &public_key.0);
|
||||
Signature(sig)
|
||||
}
|
||||
|
||||
/// Signs text with the provided Ed25519 private key, returning a base58 signature
|
||||
pub fn sign_text(&self, text: &str) -> String {
|
||||
let signature_bytes = self.sign(text.as_ref()).to_bytes();
|
||||
let signature = bs58::encode(signature_bytes).into_string();
|
||||
signature
|
||||
}
|
||||
}
|
||||
|
||||
impl PemStorableKey for PrivateKey {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod asymmetric;
|
||||
pub mod bech32_address_validation;
|
||||
pub mod crypto_hash;
|
||||
pub mod hkdf;
|
||||
pub mod hmac;
|
||||
|
||||
@@ -60,6 +60,13 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||
Arg::with_name(TESTNET_MODE_ARG_NAME)
|
||||
.long(TESTNET_MODE_ARG_NAME)
|
||||
.help("Set this gateway to work in a testnet mode that would allow clients to bypass bandwidth credential requirement")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(WALLET_ADDRESS)
|
||||
.long(WALLET_ADDRESS)
|
||||
.help("The wallet address you will use to bond this gateway, e.g. nymt1z9egw0knv47nmur0p8vk4rcx59h9gg4zuxrrr9")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::process;
|
||||
|
||||
use crate::config::Config;
|
||||
use clap::ArgMatches;
|
||||
use colored::Colorize;
|
||||
use crypto::bech32_address_validation;
|
||||
use url::Url;
|
||||
|
||||
pub(crate) mod init;
|
||||
@@ -25,6 +29,7 @@ pub(crate) const ETH_ENDPOINT: &str = "eth_endpoint";
|
||||
pub(crate) const ANNOUNCE_HOST_ARG_NAME: &str = "announce-host";
|
||||
pub(crate) const DATASTORE_PATH: &str = "datastore";
|
||||
pub(crate) const TESTNET_MODE_ARG_NAME: &str = "testnet-mode";
|
||||
pub(crate) const WALLET_ADDRESS: &str = "wallet-address";
|
||||
|
||||
fn parse_validators(raw: &str) -> Vec<Url> {
|
||||
raw.split(',')
|
||||
@@ -96,9 +101,36 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Confi
|
||||
config = config.with_eth_endpoint(String::from(eth_endpoint));
|
||||
}
|
||||
|
||||
if let Some(wallet_address) = matches.value_of(WALLET_ADDRESS) {
|
||||
let trimmed = wallet_address.trim();
|
||||
validate_bech32_address_or_exit(trimmed);
|
||||
config = config.with_wallet_address(trimmed);
|
||||
}
|
||||
|
||||
if matches.is_present(TESTNET_MODE_ARG_NAME) {
|
||||
config.with_testnet_mode(true)
|
||||
} else {
|
||||
config
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensures that a given bech32 address is valid, or exits
|
||||
pub(crate) fn validate_bech32_address_or_exit(address: &str) {
|
||||
if let Err(bech32_address_validation::Bech32Error::DecodeFailed(err)) =
|
||||
bech32_address_validation::try_bech32_decode(address)
|
||||
{
|
||||
let error_message = format!("Error: wallet address decoding failed: {}", err).red();
|
||||
println!("{}", error_message);
|
||||
println!("Exiting...");
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
if let Err(bech32_address_validation::Bech32Error::WrongPrefix(err)) =
|
||||
bech32_address_validation::validate_bech32_prefix(address)
|
||||
{
|
||||
let error_message = format!("Error: wallet address type is wrong, {}", err).red();
|
||||
println!("{}", error_message);
|
||||
println!("Exiting...");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +139,9 @@ pub async fn execute(matches: ArgMatches<'static>) {
|
||||
}
|
||||
|
||||
let mut gateway = Gateway::new(config).await;
|
||||
println!(
|
||||
"\nTo bond your gateway you will need to install the Nym wallet, go to https://nymtech.net/get-involved and select the Download button.\n\
|
||||
Select the correct version and install it to your machine. You will need to provide the following: \n ");
|
||||
gateway.print_node_details();
|
||||
|
||||
gateway.run().await;
|
||||
|
||||
@@ -51,7 +51,7 @@ pub fn command_args<'a, 'b>() -> App<'a, 'b> {
|
||||
cmd.arg(address_sign_cmd)
|
||||
}
|
||||
|
||||
fn load_identity_keys(pathfinder: &GatewayPathfinder) -> identity::KeyPair {
|
||||
pub fn load_identity_keys(pathfinder: &GatewayPathfinder) -> identity::KeyPair {
|
||||
let identity_keypair: identity::KeyPair = pemstore::load_keypair(&pemstore::KeyPairPath::new(
|
||||
pathfinder.private_identity_key().to_owned(),
|
||||
pathfinder.public_identity_key().to_owned(),
|
||||
@@ -111,45 +111,25 @@ fn sign_derived_address(private_key: &identity::PrivateKey, address: &AccountId)
|
||||
|
||||
// we do tiny bit of sanity check validation
|
||||
#[cfg(feature = "coconut")]
|
||||
fn sign_provided_address(private_key: &identity::PrivateKey, raw_address: &str) {
|
||||
fn print_signed_address(private_key: &identity::PrivateKey, raw_address: &str) -> String {
|
||||
let trimmed = raw_address.trim();
|
||||
|
||||
// try to decode the address (to make sure it's a valid bech32 encoding)
|
||||
let (prefix, _) = match bech32::decode(trimmed) {
|
||||
Ok(decoded) => decoded,
|
||||
Err(err) => {
|
||||
let error_message =
|
||||
format!("Your wallet address failed to get decoded! Are you sure you copied it correctly? The error was: {}", err).red();
|
||||
println!("{}", error_message);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
if prefix != BECH32_PREFIX {
|
||||
let error_message =
|
||||
format!("Your wallet address must start with a '{}'", BECH32_PREFIX).red();
|
||||
println!("{}", error_message);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
let signature_bytes = private_key.sign(trimmed.as_ref()).to_bytes();
|
||||
let signature = bs58::encode(signature_bytes).into_string();
|
||||
validate_bech32_address_or_exit(trimmed);
|
||||
let signature = private_key.sign_text(trimmed);
|
||||
|
||||
println!(
|
||||
"The base58-encoded signature on '{}' is: {}",
|
||||
trimmed, signature
|
||||
)
|
||||
);
|
||||
signature
|
||||
}
|
||||
|
||||
// we just sign whatever the user has provided
|
||||
fn sign_text(private_key: &identity::PrivateKey, text: &str) {
|
||||
fn print_signed_text(private_key: &identity::PrivateKey, text: &str) {
|
||||
println!(
|
||||
"Signing the text {:?} using your mixnode's Ed25519 identity key...",
|
||||
text
|
||||
);
|
||||
|
||||
let signature_bytes = private_key.sign(text.as_ref()).to_bytes();
|
||||
let signature = bs58::encode(signature_bytes).into_string();
|
||||
let signature = private_key.sign_text(text);
|
||||
|
||||
println!(
|
||||
"The base58-encoded signature on '{}' is: {}",
|
||||
@@ -171,7 +151,7 @@ pub fn execute(matches: &ArgMatches) {
|
||||
let identity_keypair = load_identity_keys(&pathfinder);
|
||||
|
||||
if let Some(text) = matches.value_of(SIGN_TEXT_ARG_NAME) {
|
||||
sign_text(identity_keypair.private_key(), text)
|
||||
print_signed_text(identity_keypair.private_key(), text)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
|
||||
@@ -193,6 +193,11 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_wallet_address(mut self, wallet_address: &str) -> Self {
|
||||
self.gateway.wallet_address = wallet_address.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
// getters
|
||||
pub fn get_config_file_save_location(&self) -> PathBuf {
|
||||
self.config_directory().join(Self::config_file_name())
|
||||
@@ -280,6 +285,10 @@ impl Config {
|
||||
pub fn get_version(&self) -> &str {
|
||||
&self.gateway.version
|
||||
}
|
||||
|
||||
pub fn get_wallet_address(&self) -> &str {
|
||||
&self.gateway.wallet_address
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
@@ -350,6 +359,9 @@ pub struct Gateway {
|
||||
/// Path to sqlite database containing all persistent data: messages for offline clients,
|
||||
/// derived shared keys and available client bandwidths.
|
||||
persistent_storage: PathBuf,
|
||||
|
||||
/// The Cosmos wallet address that will control this gateway
|
||||
wallet_address: String,
|
||||
}
|
||||
|
||||
impl Gateway {
|
||||
@@ -397,6 +409,7 @@ impl Default for Gateway {
|
||||
cosmos_mnemonic: "".to_string(),
|
||||
nym_root_directory: Config::default_root_directory(),
|
||||
persistent_storage: Default::default(),
|
||||
wallet_address: "nymXXXXXXXX".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,9 @@ validator_nymd_urls = [
|
||||
|
||||
cosmos_mnemonic = "{{ gateway.cosmos_mnemonic }}"
|
||||
|
||||
# Nym wallet address on the blockchain that should control this gateway
|
||||
wallet_address = '{{ gateway.wallet_address }}'
|
||||
|
||||
##### advanced configuration options #####
|
||||
|
||||
# nym_home_directory specifies absolute path to the home nym gateway directory.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::commands::sign::load_identity_keys;
|
||||
use crate::commands::validate_bech32_address_or_exit;
|
||||
use crate::config::Config;
|
||||
use crate::node::client_handling::active_clients::ActiveClientsStore;
|
||||
use crate::node::client_handling::websocket;
|
||||
@@ -78,6 +80,17 @@ impl Gateway {
|
||||
sphinx_keypair
|
||||
}
|
||||
|
||||
/// Signs the node config's bech32 address to produce a verification code for use in the wallet.
|
||||
/// Exits if the address isn't valid (which should protect against manual edits).
|
||||
fn generate_verification_code(&self) -> String {
|
||||
let pathfinder = GatewayPathfinder::new_from_config(&self.config);
|
||||
let identity_keypair = load_identity_keys(&pathfinder);
|
||||
let address = self.config.get_wallet_address();
|
||||
validate_bech32_address_or_exit(address);
|
||||
let verification_code = identity_keypair.private_key().sign_text(address);
|
||||
verification_code
|
||||
}
|
||||
|
||||
pub(crate) fn print_node_details(&self) {
|
||||
println!(
|
||||
"Identity Key: {}",
|
||||
@@ -87,6 +100,10 @@ impl Gateway {
|
||||
"Sphinx Key: {}",
|
||||
self.sphinx_keypair.public_key().to_base58_string()
|
||||
);
|
||||
println!(
|
||||
"Node Verification Code: {}",
|
||||
self.generate_verification_code()
|
||||
);
|
||||
println!(
|
||||
"Host: {} (bind address: {})",
|
||||
self.config.get_announce_address(),
|
||||
|
||||
@@ -27,7 +27,6 @@ pretty_env_logger = "0.4.0"
|
||||
rand = "0.7.3"
|
||||
rocket = { version="0.5.0-rc.1", features = ["json"] }
|
||||
serde = { version="1.0", features = ["derive"] }
|
||||
subtle-encoding = { version = "0.5", features = ["bech32-preview"]}
|
||||
tokio = { version="1.8", features = ["rt-multi-thread", "net", "signal"] }
|
||||
tokio-util = { version="0.6.7", features = ["codec"] }
|
||||
toml = "0.5.8"
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
|
||||
use std::process;
|
||||
|
||||
use crate::{config::Config, crypto::bech32_address_validation};
|
||||
use crate::config::Config;
|
||||
use clap::ArgMatches;
|
||||
use colored::Colorize;
|
||||
use crypto::bech32_address_validation;
|
||||
use url::Url;
|
||||
|
||||
pub(crate) mod describe;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
use crate::commands::*;
|
||||
use crate::config::{persistence::pathfinder::MixNodePathfinder, Config};
|
||||
use crate::crypto::ed25519::sign_text;
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use colored::Colorize;
|
||||
use config::NymConfig;
|
||||
@@ -51,7 +50,7 @@ pub fn load_identity_keys(pathfinder: &MixNodePathfinder) -> identity::KeyPair {
|
||||
fn print_signed_address(private_key: &identity::PrivateKey, raw_address: &str) -> String {
|
||||
let trimmed = raw_address.trim();
|
||||
validate_bech32_address_or_exit(trimmed);
|
||||
let signature = sign_text(private_key, trimmed);
|
||||
let signature = private_key.sign_text(trimmed);
|
||||
|
||||
println!(
|
||||
"The base58-encoded signature on '{}' is: {}",
|
||||
@@ -66,7 +65,7 @@ fn print_signed_text(private_key: &identity::PrivateKey, text: &str) {
|
||||
text
|
||||
);
|
||||
|
||||
let signature = sign_text(private_key, text);
|
||||
let signature = private_key.sign_text(text);
|
||||
|
||||
println!(
|
||||
"The base58-encoded signature on '{}' is: {}",
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
use crypto::asymmetric::identity;
|
||||
|
||||
/// Signs text with the provided Ed25519 private key
|
||||
pub fn sign_text(private_key: &identity::PrivateKey, text: &str) -> String {
|
||||
let signature_bytes = private_key.sign(text.as_ref()).to_bytes();
|
||||
let signature = bs58::encode(signature_bytes).into_string();
|
||||
signature
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
pub mod bech32_address_validation;
|
||||
pub mod ed25519;
|
||||
@@ -8,7 +8,6 @@ use clap::{crate_version, App, ArgMatches};
|
||||
|
||||
mod commands;
|
||||
mod config;
|
||||
mod crypto;
|
||||
mod node;
|
||||
|
||||
fn main() {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
use crate::commands::validate_bech32_address_or_exit;
|
||||
use crate::config::Config;
|
||||
use crate::crypto::ed25519::sign_text;
|
||||
use crate::node::http::{
|
||||
description::description,
|
||||
not_found,
|
||||
@@ -91,7 +90,7 @@ impl MixNode {
|
||||
let identity_keypair = load_identity_keys(&pathfinder);
|
||||
let address = self.config.get_wallet_address();
|
||||
validate_bech32_address_or_exit(address);
|
||||
let verification_code = sign_text(identity_keypair.private_key(), address);
|
||||
let verification_code = identity_keypair.private_key().sign_text(address);
|
||||
verification_code
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user