mirror of
https://github.com/mimblewimble/mwixnet.git
synced 2025-01-20 19:11:09 +03:00
support api secrets, handle SIGQUIT, SIGINT, and SIGTERM
This commit is contained in:
parent
40bf97a841
commit
e902f78e32
8 changed files with 132 additions and 22 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2475,6 +2475,7 @@ dependencies = [
|
|||
"bytes 0.5.6",
|
||||
"chacha20",
|
||||
"clap",
|
||||
"dirs",
|
||||
"failure",
|
||||
"futures 0.3.17",
|
||||
"grin_api 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)",
|
||||
|
|
|
@ -11,6 +11,7 @@ byteorder = "1"
|
|||
bytes = "0.5.6"
|
||||
chacha20 = "0.8.1"
|
||||
clap = { version = "2.33", features = ["yaml"] }
|
||||
dirs = "2.0"
|
||||
failure = "0.1.8"
|
||||
futures = "0.3"
|
||||
hmac = { version = "0.12.0", features = ["std"]}
|
||||
|
|
|
@ -13,11 +13,19 @@ args:
|
|||
short: n
|
||||
long: grin_node_url
|
||||
takes_value: true
|
||||
- grin_node_secret_path:
|
||||
help: Path to a file containing the secret for the GRIN node api
|
||||
long: grin_node_secret_path
|
||||
takes_value: true
|
||||
- wallet_owner_url:
|
||||
help: Api address of running wallet owner listener
|
||||
short: l
|
||||
long: wallet_owner_url
|
||||
takes_value: true
|
||||
- wallet_owner_secret_path:
|
||||
help: Path to a file containing the secret for the wallet owner api
|
||||
long: wallet_owner_secret_path
|
||||
takes_value: true
|
||||
- wallet_pass:
|
||||
help: The wallet's password
|
||||
long: wallet_pass
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::error::{self, Result};
|
|||
use crate::secp::SecretKey;
|
||||
|
||||
use core::num::NonZeroU32;
|
||||
use grin_util::{ToHex, ZeroingString};
|
||||
use grin_util::{file, ToHex, ZeroingString};
|
||||
use rand::{thread_rng, Rng};
|
||||
use ring::{aead, pbkdf2};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
@ -11,6 +11,11 @@ use std::io::prelude::*;
|
|||
use std::net::SocketAddr;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const GRIN_HOME: &str = ".grin";
|
||||
const CHAIN_NAME: &str = "main";
|
||||
const NODE_API_SECRET_FILE_NAME: &str = ".api_secret";
|
||||
const WALLET_OWNER_API_SECRET_FILE_NAME: &str = ".owner_api_secret";
|
||||
|
||||
/// The decrypted server config to be passed around and used by the rest of the mwixnet code
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct ServerConfig {
|
||||
|
@ -22,8 +27,22 @@ pub struct ServerConfig {
|
|||
pub addr: SocketAddr,
|
||||
/// foreign api address of the grin node
|
||||
pub grin_node_url: SocketAddr,
|
||||
/// path to file containing api secret for the grin node
|
||||
pub grin_node_secret_path: Option<String>,
|
||||
/// owner api address of the grin wallet
|
||||
pub wallet_owner_url: SocketAddr,
|
||||
/// path to file containing secret for the grin wallet's owner api
|
||||
pub wallet_owner_secret_path: Option<String>,
|
||||
}
|
||||
|
||||
impl ServerConfig {
|
||||
pub fn node_api_secret(&self) -> Option<String> {
|
||||
file::get_first_line(self.grin_node_secret_path.clone())
|
||||
}
|
||||
|
||||
pub fn wallet_owner_api_secret(&self) -> Option<String> {
|
||||
file::get_first_line(self.wallet_owner_secret_path.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// Encrypted server key, for storing on disk and decrypting with a password.
|
||||
|
@ -134,7 +153,9 @@ struct RawConfig {
|
|||
interval_s: u32,
|
||||
addr: SocketAddr,
|
||||
grin_node_url: SocketAddr,
|
||||
grin_node_secret_path: Option<String>,
|
||||
wallet_owner_url: SocketAddr,
|
||||
wallet_owner_secret_path: Option<String>,
|
||||
}
|
||||
|
||||
/// Writes the server config to the config_path given, encrypting the server_key first.
|
||||
|
@ -152,7 +173,9 @@ pub fn write_config(
|
|||
interval_s: server_config.interval_s,
|
||||
addr: server_config.addr,
|
||||
grin_node_url: server_config.grin_node_url,
|
||||
grin_node_secret_path: server_config.grin_node_secret_path.clone(),
|
||||
wallet_owner_url: server_config.wallet_owner_url,
|
||||
wallet_owner_secret_path: server_config.wallet_owner_secret_path.clone(),
|
||||
};
|
||||
let encoded: String = toml::to_string(&raw_config).map_err(|e| {
|
||||
error::ErrorKind::SaveConfigError(format!("Error while encoding config as toml: {}", e))
|
||||
|
@ -189,10 +212,34 @@ pub fn load_config(config_path: &PathBuf, password: &ZeroingString) -> Result<Se
|
|||
interval_s: raw_config.interval_s,
|
||||
addr: raw_config.addr,
|
||||
grin_node_url: raw_config.grin_node_url,
|
||||
grin_node_secret_path: raw_config.grin_node_secret_path,
|
||||
wallet_owner_url: raw_config.wallet_owner_url,
|
||||
wallet_owner_secret_path: raw_config.wallet_owner_secret_path,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_grin_path() -> PathBuf {
|
||||
let mut grin_path = match dirs::home_dir() {
|
||||
Some(p) => p,
|
||||
None => PathBuf::new(),
|
||||
};
|
||||
grin_path.push(GRIN_HOME);
|
||||
grin_path.push(CHAIN_NAME);
|
||||
grin_path
|
||||
}
|
||||
|
||||
pub fn node_secret_path() -> PathBuf {
|
||||
let mut path = get_grin_path();
|
||||
path.push(NODE_API_SECRET_FILE_NAME);
|
||||
path
|
||||
}
|
||||
|
||||
pub fn wallet_owner_secret_path() -> PathBuf {
|
||||
let mut path = get_grin_path();
|
||||
path.push(WALLET_OWNER_API_SECRET_FILE_NAME);
|
||||
path
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
75
src/main.rs
75
src/main.rs
|
@ -1,13 +1,10 @@
|
|||
use config::ServerConfig;
|
||||
use error::{Error, ErrorKind};
|
||||
use node::HttpGrinNode;
|
||||
use wallet::HttpWallet;
|
||||
|
||||
use clap::App;
|
||||
use grin_util::{StopState, ZeroingString};
|
||||
use rpassword;
|
||||
use std::env;
|
||||
use std::io::{self, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tokio::runtime::Runtime;
|
||||
|
@ -41,9 +38,9 @@ fn real_main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
let config_path = match args.value_of("config_file") {
|
||||
Some(path) => PathBuf::from(path),
|
||||
None => {
|
||||
let mut current_dir = env::current_dir()?;
|
||||
current_dir.push("mwixnet-config.toml");
|
||||
current_dir
|
||||
let mut grin_path = config::get_grin_path();
|
||||
grin_path.push("mwixnet-config.toml");
|
||||
grin_path
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -52,7 +49,9 @@ fn real_main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.map(|t| t.parse::<u32>().unwrap());
|
||||
let bind_addr = args.value_of("bind_addr");
|
||||
let grin_node_url = args.value_of("grin_node_url");
|
||||
let grin_node_secret_path = args.value_of("grin_node_secret_path");
|
||||
let wallet_owner_url = args.value_of("wallet_owner_url");
|
||||
let wallet_owner_secret_path = args.value_of("wallet_owner_secret_path");
|
||||
|
||||
// Write a new config file if init-config command is supplied
|
||||
if let ("init-config", Some(_)) = args.subcommand() {
|
||||
|
@ -68,7 +67,17 @@ fn real_main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
interval_s: round_time.unwrap_or(DEFAULT_INTERVAL),
|
||||
addr: bind_addr.unwrap_or("0.0.0.0:3000").parse()?,
|
||||
grin_node_url: grin_node_url.unwrap_or("127.0.0.1:3413").parse()?,
|
||||
grin_node_secret_path: match grin_node_secret_path {
|
||||
Some(p) => Some(p.to_owned()),
|
||||
None => config::node_secret_path().to_str().map(|p| p.to_owned()),
|
||||
},
|
||||
wallet_owner_url: wallet_owner_url.unwrap_or("127.0.0.1:3420").parse()?,
|
||||
wallet_owner_secret_path: match wallet_owner_secret_path {
|
||||
Some(p) => Some(p.to_owned()),
|
||||
None => config::wallet_owner_secret_path()
|
||||
.to_str()
|
||||
.map(|p| p.to_owned()),
|
||||
},
|
||||
};
|
||||
|
||||
let password = prompt_password_confirm();
|
||||
|
@ -93,31 +102,41 @@ fn real_main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
server_config.grin_node_url = grin_node_url.parse()?;
|
||||
}
|
||||
|
||||
// Override grin_node_secret_path, if supplied
|
||||
if let Some(grin_node_secret_path) = grin_node_secret_path {
|
||||
server_config.grin_node_secret_path = Some(grin_node_secret_path.to_owned());
|
||||
}
|
||||
|
||||
// Override wallet_owner_url, if supplied
|
||||
if let Some(wallet_owner_url) = wallet_owner_url {
|
||||
server_config.wallet_owner_url = wallet_owner_url.parse()?;
|
||||
}
|
||||
|
||||
// Override wallet_owner_secret_path, if supplied
|
||||
if let Some(wallet_owner_secret_path) = wallet_owner_secret_path {
|
||||
server_config.wallet_owner_secret_path = Some(wallet_owner_secret_path.to_owned());
|
||||
}
|
||||
|
||||
// Open wallet
|
||||
let wallet_pass = prompt_wallet_password(&args.value_of("wallet_pass"));
|
||||
let wallet = HttpWallet::open_wallet(&server_config.wallet_owner_url, &wallet_pass)?;
|
||||
let wallet = HttpWallet::open_wallet(
|
||||
&server_config.wallet_owner_url,
|
||||
&server_config.wallet_owner_api_secret(),
|
||||
&wallet_pass,
|
||||
)?;
|
||||
|
||||
// Create GrinNode
|
||||
let node = HttpGrinNode::new(&server_config.grin_node_url, &None);
|
||||
let node = HttpGrinNode::new(
|
||||
&server_config.grin_node_url,
|
||||
&server_config.node_api_secret(),
|
||||
);
|
||||
|
||||
let stop_state = Arc::new(StopState::new());
|
||||
|
||||
let stop_state_clone = stop_state.clone();
|
||||
|
||||
let rt = Runtime::new()?;
|
||||
rt.spawn(async move {
|
||||
let shutdown_signal = async move {
|
||||
// Wait for the CTRL+C signal
|
||||
tokio::signal::ctrl_c()
|
||||
.await
|
||||
.expect("failed to install CTRL+C signal handler");
|
||||
};
|
||||
futures::executor::block_on(shutdown_signal);
|
||||
futures::executor::block_on(build_signals_fut());
|
||||
stop_state_clone.stop();
|
||||
});
|
||||
|
||||
|
@ -130,6 +149,30 @@ fn real_main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
)
|
||||
}
|
||||
|
||||
async fn build_signals_fut() {
|
||||
if cfg!(unix) {
|
||||
use tokio::signal::unix::{signal, SignalKind};
|
||||
|
||||
// Listen for SIGINT, SIGQUIT, and SIGTERM
|
||||
let mut terminate_signal =
|
||||
signal(SignalKind::terminate()).expect("failed to create terminate signal");
|
||||
let mut quit_signal = signal(SignalKind::quit()).expect("failed to create quit signal");
|
||||
let mut interrupt_signal =
|
||||
signal(SignalKind::interrupt()).expect("failed to create interrupt signal");
|
||||
|
||||
futures::future::select_all(vec![
|
||||
Box::pin(terminate_signal.recv()),
|
||||
Box::pin(quit_signal.recv()),
|
||||
Box::pin(interrupt_signal.recv()),
|
||||
])
|
||||
.await;
|
||||
} else {
|
||||
tokio::signal::ctrl_c()
|
||||
.await
|
||||
.expect("failed to install CTRL+C signal handler");
|
||||
}
|
||||
}
|
||||
|
||||
fn prompt_password() -> ZeroingString {
|
||||
ZeroingString::from(rpassword::prompt_password_stdout("Server password: ").unwrap())
|
||||
}
|
||||
|
|
|
@ -116,15 +116,14 @@ pub mod comsig_serde {
|
|||
}
|
||||
|
||||
/// Creates a ComSignature from a hex string
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> std::result::Result<ComSignature, D::Error>
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<ComSignature, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
use serde::de::Error;
|
||||
let bytes = String::deserialize(deserializer)
|
||||
.and_then(|string| grin_util::from_hex(&string).map_err(Error::custom))?;
|
||||
let sig: ComSignature =
|
||||
grin_core::ser::deserialize_default(&mut &bytes[..]).map_err(Error::custom)?;
|
||||
let sig: ComSignature = ser::deserialize_default(&mut &bytes[..]).map_err(Error::custom)?;
|
||||
Ok(sig)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -319,7 +319,9 @@ mod tests {
|
|||
interval_s: 1,
|
||||
addr: TcpListener::bind("127.0.0.1:0")?.local_addr()?,
|
||||
grin_node_url: "127.0.0.1:3413".parse()?,
|
||||
grin_node_secret_path: None,
|
||||
wallet_owner_url: "127.0.0.1:3420".parse()?,
|
||||
wallet_owner_secret_path: None,
|
||||
};
|
||||
|
||||
let threaded_rt = runtime::Runtime::new()?;
|
||||
|
|
|
@ -85,6 +85,7 @@ pub fn assemble_tx(
|
|||
#[derive(Clone)]
|
||||
pub struct HttpWallet {
|
||||
wallet_owner_url: SocketAddr,
|
||||
wallet_owner_secret: Option<String>,
|
||||
shared_key: SecretKey,
|
||||
token: Token,
|
||||
}
|
||||
|
@ -104,6 +105,7 @@ impl HttpWallet {
|
|||
/// Calls the 'open_wallet' using the RPC API.
|
||||
pub fn open_wallet(
|
||||
wallet_owner_url: &SocketAddr,
|
||||
wallet_owner_secret: &Option<String>,
|
||||
wallet_pass: &ZeroingString,
|
||||
) -> Result<HttpWallet> {
|
||||
println!("Opening wallet at {}", wallet_owner_url);
|
||||
|
@ -115,6 +117,7 @@ impl HttpWallet {
|
|||
});
|
||||
let token: Token = HttpWallet::send_enc_request(
|
||||
&wallet_owner_url,
|
||||
&wallet_owner_secret,
|
||||
"open_wallet",
|
||||
&open_wallet_params,
|
||||
&shared_key,
|
||||
|
@ -123,6 +126,7 @@ impl HttpWallet {
|
|||
|
||||
Ok(HttpWallet {
|
||||
wallet_owner_url: wallet_owner_url.clone(),
|
||||
wallet_owner_secret: wallet_owner_secret.clone(),
|
||||
shared_key: shared_key.clone(),
|
||||
token: token.clone(),
|
||||
})
|
||||
|
@ -152,6 +156,7 @@ impl HttpWallet {
|
|||
|
||||
fn send_enc_request<D: serde::de::DeserializeOwned>(
|
||||
wallet_owner_url: &SocketAddr,
|
||||
wallet_owner_secret: &Option<String>,
|
||||
method: &str,
|
||||
params: &serde_json::Value,
|
||||
shared_key: &SecretKey,
|
||||
|
@ -164,8 +169,11 @@ impl HttpWallet {
|
|||
"jsonrpc": "2.0",
|
||||
});
|
||||
let enc_req = EncryptedRequest::from_json(&JsonId::IntId(1), &req, &shared_key)?;
|
||||
let res =
|
||||
client::post::<EncryptedRequest, EncryptedResponse>(url.as_str(), None, &enc_req)?;
|
||||
let res = client::post::<EncryptedRequest, EncryptedResponse>(
|
||||
url.as_str(),
|
||||
wallet_owner_secret.clone(),
|
||||
&enc_req,
|
||||
)?;
|
||||
let decrypted = res.decrypt(&shared_key)?;
|
||||
let response: Response = serde_json::from_value(decrypted.clone())?;
|
||||
let parsed = serde_json::from_value(response.result.unwrap().get("Ok").unwrap().clone())?;
|
||||
|
@ -205,6 +213,7 @@ impl Wallet for HttpWallet {
|
|||
});
|
||||
let output: OutputWithBlind = HttpWallet::send_enc_request(
|
||||
&self.wallet_owner_url,
|
||||
&self.wallet_owner_secret,
|
||||
"build_output",
|
||||
&req_json,
|
||||
&self.shared_key,
|
||||
|
|
Loading…
Reference in a new issue