mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-01 08:51:08 +03:00
feat: Configuration option to include foreign API endpoints in the owner API (#2305)
* Allow foreign API over owner API port * Tests for owner_api_include_foreign config option
This commit is contained in:
parent
0b73e87f8c
commit
a3431fb147
8 changed files with 136 additions and 13 deletions
|
@ -390,6 +390,15 @@ fn comments() -> HashMap<String, String> {
|
||||||
"node_api_secret_path".to_string(),
|
"node_api_secret_path".to_string(),
|
||||||
"
|
"
|
||||||
#location of the node api secret for basic auth on the Grin API
|
#location of the node api secret for basic auth on the Grin API
|
||||||
|
"
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
retval.insert(
|
||||||
|
"owner_api_include_foreign".to_string(),
|
||||||
|
"
|
||||||
|
#include the foreign API endpoints on the same port as the owner
|
||||||
|
#API. Useful for networking environments like AWS ECS that make
|
||||||
|
#it difficult to access multiple ports on a single service.
|
||||||
"
|
"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
);
|
);
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
1. [POST Cancel Tx](#post-cancel-tx)
|
1. [POST Cancel Tx](#post-cancel-tx)
|
||||||
1. [POST Post Tx](#post-post-tx)
|
1. [POST Post Tx](#post-post-tx)
|
||||||
1. [POST Issue Burn Tx](#post-issue-burn-tx)
|
1. [POST Issue Burn Tx](#post-issue-burn-tx)
|
||||||
|
1. [Adding Foreign API Endpoints](#add-foreign-api-endpoints)
|
||||||
|
|
||||||
## Wallet Owner Endpoint
|
## Wallet Owner Endpoint
|
||||||
|
|
||||||
|
@ -681,3 +682,7 @@ Issue a burn TX.
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Add Foreign API Endpoints
|
||||||
|
|
||||||
|
For some environments it may be convenient to have the [wallet foreign API](wallet_foreign_api.md) available on the same port as the owner API. You can do so by setting the `owner_api_include_foreign` configuration setting to `true`.
|
||||||
|
|
|
@ -67,6 +67,12 @@ pub struct LocalServerContainerConfig {
|
||||||
// Port the wallet server is running on
|
// Port the wallet server is running on
|
||||||
pub wallet_port: u16,
|
pub wallet_port: u16,
|
||||||
|
|
||||||
|
// Port the wallet owner API is running on
|
||||||
|
pub owner_port: u16,
|
||||||
|
|
||||||
|
// Whether to include the foreign API endpoints in the owner API
|
||||||
|
pub owner_api_include_foreign: bool,
|
||||||
|
|
||||||
// Whether we're going to mine
|
// Whether we're going to mine
|
||||||
pub start_miner: bool,
|
pub start_miner: bool,
|
||||||
|
|
||||||
|
@ -104,6 +110,8 @@ impl Default for LocalServerContainerConfig {
|
||||||
api_server_port: 13413,
|
api_server_port: 13413,
|
||||||
p2p_server_port: 13414,
|
p2p_server_port: 13414,
|
||||||
wallet_port: 13415,
|
wallet_port: 13415,
|
||||||
|
owner_port: 13420,
|
||||||
|
owner_api_include_foreign: false,
|
||||||
seed_addr: String::from(""),
|
seed_addr: String::from(""),
|
||||||
is_seeding: false,
|
is_seeding: false,
|
||||||
start_miner: false,
|
start_miner: false,
|
||||||
|
@ -161,6 +169,7 @@ impl LocalServerContainer {
|
||||||
|
|
||||||
wallet_config.api_listen_port = config.wallet_port;
|
wallet_config.api_listen_port = config.wallet_port;
|
||||||
wallet_config.check_node_api_http_addr = config.wallet_validating_node_url.clone();
|
wallet_config.check_node_api_http_addr = config.wallet_validating_node_url.clone();
|
||||||
|
wallet_config.owner_api_include_foreign = Some(config.owner_api_include_foreign);
|
||||||
wallet_config.data_file_dir = working_dir.clone();
|
wallet_config.data_file_dir = working_dir.clone();
|
||||||
Ok(LocalServerContainer {
|
Ok(LocalServerContainer {
|
||||||
config: config,
|
config: config,
|
||||||
|
@ -237,10 +246,10 @@ impl LocalServerContainer {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Starts a wallet daemon to receive and returns the
|
/// Make a wallet for use in test endpoints (run_wallet and run_owner).
|
||||||
/// listening server url
|
fn make_wallet_for_tests(
|
||||||
|
&mut self,
|
||||||
pub fn run_wallet(&mut self, _duration_in_mills: u64) {
|
) -> Arc<Mutex<LMDBBackend<HTTPNodeClient, keychain::ExtKeychain>>> {
|
||||||
// URL on which to start the wallet listener (i.e. api server)
|
// URL on which to start the wallet listener (i.e. api server)
|
||||||
let _url = format!("{}:{}", self.config.base_addr, self.config.wallet_port);
|
let _url = format!("{}:{}", self.config.base_addr, self.config.wallet_port);
|
||||||
|
|
||||||
|
@ -260,6 +269,7 @@ impl LocalServerContainer {
|
||||||
self.wallet_config.check_node_api_http_addr =
|
self.wallet_config.check_node_api_http_addr =
|
||||||
self.config.wallet_validating_node_url.clone();
|
self.config.wallet_validating_node_url.clone();
|
||||||
self.wallet_config.data_file_dir = self.working_dir.clone();
|
self.wallet_config.data_file_dir = self.working_dir.clone();
|
||||||
|
self.wallet_config.owner_api_include_foreign = Some(self.config.owner_api_include_foreign);
|
||||||
|
|
||||||
let _ = fs::create_dir_all(self.wallet_config.clone().data_file_dir);
|
let _ = fs::create_dir_all(self.wallet_config.clone().data_file_dir);
|
||||||
let r = wallet::WalletSeed::init_file(&self.wallet_config, 32, "");
|
let r = wallet::WalletSeed::init_file(&self.wallet_config, 32, "");
|
||||||
|
@ -278,19 +288,45 @@ impl LocalServerContainer {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet::controller::foreign_listener(
|
Arc::new(Mutex::new(wallet))
|
||||||
Arc::new(Mutex::new(wallet)),
|
}
|
||||||
&self.wallet_config.api_listen_addr(),
|
|
||||||
|
/// Starts a wallet daemon to receive
|
||||||
|
pub fn run_wallet(&mut self, _duration_in_mills: u64) {
|
||||||
|
let wallet = self.make_wallet_for_tests();
|
||||||
|
|
||||||
|
wallet::controller::foreign_listener(wallet, &self.wallet_config.api_listen_addr(), None)
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
panic!(
|
||||||
|
"Error creating wallet listener: {:?} Config: {:?}",
|
||||||
|
e, self.wallet_config
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
self.wallet_is_running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Starts a wallet owner daemon
|
||||||
|
pub fn run_owner(&mut self) {
|
||||||
|
let wallet = self.make_wallet_for_tests();
|
||||||
|
|
||||||
|
// WalletConfig doesn't allow changing the owner API path, so we build
|
||||||
|
// the path ourselves
|
||||||
|
let owner_listen_addr = format!("127.0.0.1:{}", self.config.owner_port);
|
||||||
|
|
||||||
|
wallet::controller::owner_listener(
|
||||||
|
wallet,
|
||||||
|
&owner_listen_addr,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
|
self.wallet_config.owner_api_include_foreign.clone(),
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
panic!(
|
panic!(
|
||||||
"Error creating wallet listener: {:?} Config: {:?}",
|
"Error creating wallet owner listener: {:?} Config: {:?}",
|
||||||
e, self.wallet_config
|
e, self.wallet_config
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
self.wallet_is_running = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_wallet_seed(config: &WalletConfig) -> wallet::WalletSeed {
|
pub fn get_wallet_seed(config: &WalletConfig) -> wallet::WalletSeed {
|
||||||
|
@ -403,6 +439,10 @@ pub struct LocalServerContainerPoolConfig {
|
||||||
//
|
//
|
||||||
pub base_wallet_port: u16,
|
pub base_wallet_port: u16,
|
||||||
|
|
||||||
|
// Base wallet owner port for this server
|
||||||
|
//
|
||||||
|
pub base_owner_port: u16,
|
||||||
|
|
||||||
// How long the servers in the pool are going to run
|
// How long the servers in the pool are going to run
|
||||||
pub run_length_in_seconds: u64,
|
pub run_length_in_seconds: u64,
|
||||||
}
|
}
|
||||||
|
@ -417,6 +457,7 @@ impl Default for LocalServerContainerPoolConfig {
|
||||||
base_p2p_port: 10000,
|
base_p2p_port: 10000,
|
||||||
base_api_port: 11000,
|
base_api_port: 11000,
|
||||||
base_wallet_port: 12000,
|
base_wallet_port: 12000,
|
||||||
|
base_owner_port: 13000,
|
||||||
run_length_in_seconds: 30,
|
run_length_in_seconds: 30,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -439,6 +480,8 @@ pub struct LocalServerContainerPool {
|
||||||
|
|
||||||
next_wallet_port: u16,
|
next_wallet_port: u16,
|
||||||
|
|
||||||
|
next_owner_port: u16,
|
||||||
|
|
||||||
// keep track of whether a seed exists, and pause a bit if so
|
// keep track of whether a seed exists, and pause a bit if so
|
||||||
is_seeding: bool,
|
is_seeding: bool,
|
||||||
}
|
}
|
||||||
|
@ -449,6 +492,7 @@ impl LocalServerContainerPool {
|
||||||
next_api_port: config.base_api_port,
|
next_api_port: config.base_api_port,
|
||||||
next_p2p_port: config.base_p2p_port,
|
next_p2p_port: config.base_p2p_port,
|
||||||
next_wallet_port: config.base_wallet_port,
|
next_wallet_port: config.base_wallet_port,
|
||||||
|
next_owner_port: config.base_owner_port,
|
||||||
config: config,
|
config: config,
|
||||||
server_containers: Vec::new(),
|
server_containers: Vec::new(),
|
||||||
is_seeding: false,
|
is_seeding: false,
|
||||||
|
@ -466,6 +510,7 @@ impl LocalServerContainerPool {
|
||||||
server_config.p2p_server_port = self.next_p2p_port;
|
server_config.p2p_server_port = self.next_p2p_port;
|
||||||
server_config.api_server_port = self.next_api_port;
|
server_config.api_server_port = self.next_api_port;
|
||||||
server_config.wallet_port = self.next_wallet_port;
|
server_config.wallet_port = self.next_wallet_port;
|
||||||
|
server_config.owner_port = self.next_owner_port;
|
||||||
|
|
||||||
server_config.name = String::from(format!(
|
server_config.name = String::from(format!(
|
||||||
"{}/{}-{}",
|
"{}/{}-{}",
|
||||||
|
@ -481,6 +526,7 @@ impl LocalServerContainerPool {
|
||||||
self.next_p2p_port += 1;
|
self.next_p2p_port += 1;
|
||||||
self.next_api_port += 1;
|
self.next_api_port += 1;
|
||||||
self.next_wallet_port += 1;
|
self.next_wallet_port += 1;
|
||||||
|
self.next_owner_port += 1;
|
||||||
|
|
||||||
if server_config.is_seeding {
|
if server_config.is_seeding {
|
||||||
self.is_seeding = true;
|
self.is_seeding = true;
|
||||||
|
|
|
@ -21,6 +21,7 @@ use self::util::Mutex;
|
||||||
use crate::framework::{LocalServerContainer, LocalServerContainerConfig};
|
use crate::framework::{LocalServerContainer, LocalServerContainerConfig};
|
||||||
use grin_core as core;
|
use grin_core as core;
|
||||||
use grin_util as util;
|
use grin_util as util;
|
||||||
|
use grin_wallet as wallet;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{thread, time};
|
use std::{thread, time};
|
||||||
|
|
||||||
|
@ -152,3 +153,50 @@ fn basic_wallet_transactions() {
|
||||||
LocalServerContainer::get_wallet_info(&coinbase_wallet_config, &coinbase_seed);
|
LocalServerContainer::get_wallet_info(&coinbase_wallet_config, &coinbase_seed);
|
||||||
println!("Coinbase wallet info final: {:?}", coinbase_info);
|
println!("Coinbase wallet info final: {:?}", coinbase_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tests the owner_api_include_foreign configuration option.
|
||||||
|
#[test]
|
||||||
|
fn wallet_config_owner_api_include_foreign() {
|
||||||
|
// Test setup
|
||||||
|
let test_name_dir = "test_servers";
|
||||||
|
core::global::set_mining_mode(core::global::ChainTypes::AutomatedTesting);
|
||||||
|
framework::clean_all_output(test_name_dir);
|
||||||
|
let mut log_config = util::LoggingConfig::default();
|
||||||
|
log_config.stdout_log_level = util::LogLevel::Info;
|
||||||
|
util::init_test_logger();
|
||||||
|
|
||||||
|
// This is just used for testing whether the API endpoint exists
|
||||||
|
// so we have nonsense values
|
||||||
|
let block_fees = wallet::BlockFees {
|
||||||
|
fees: 1,
|
||||||
|
height: 2,
|
||||||
|
key_id: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut base_config = LocalServerContainerConfig::default();
|
||||||
|
base_config.name = String::from("test_owner_api_include_foreign");
|
||||||
|
base_config.start_wallet = true;
|
||||||
|
|
||||||
|
// Start up the wallet owner API with the default config, and make sure
|
||||||
|
// we get an error when trying to hit the coinbase endpoint
|
||||||
|
let mut default_config = base_config.clone();
|
||||||
|
default_config.owner_port = 20005;
|
||||||
|
let _ = thread::spawn(move || {
|
||||||
|
let mut default_owner = LocalServerContainer::new(default_config).unwrap();
|
||||||
|
default_owner.run_owner();
|
||||||
|
});
|
||||||
|
thread::sleep(time::Duration::from_millis(1000));
|
||||||
|
assert!(wallet::create_coinbase("http://127.0.0.1:20005", &block_fees).is_err());
|
||||||
|
|
||||||
|
// Start up the wallet owner API with the owner_api_include_foreign setting set,
|
||||||
|
// and confirm that we can hit the endpoint
|
||||||
|
let mut foreign_config = base_config.clone();
|
||||||
|
foreign_config.owner_port = 20006;
|
||||||
|
foreign_config.owner_api_include_foreign = true;
|
||||||
|
let _ = thread::spawn(move || {
|
||||||
|
let mut owner_with_foreign = LocalServerContainer::new(foreign_config).unwrap();
|
||||||
|
owner_with_foreign.run_owner();
|
||||||
|
});
|
||||||
|
thread::sleep(time::Duration::from_millis(1000));
|
||||||
|
assert!(wallet::create_coinbase("http://127.0.0.1:20006", &block_fees).is_ok());
|
||||||
|
}
|
||||||
|
|
|
@ -494,9 +494,9 @@ pub fn wallet_command(
|
||||||
("owner_api", Some(_)) => {
|
("owner_api", Some(_)) => {
|
||||||
let mut g = global_wallet_args.clone();
|
let mut g = global_wallet_args.clone();
|
||||||
g.tls_conf = None;
|
g.tls_conf = None;
|
||||||
command::owner_api(inst_wallet(), &g)
|
command::owner_api(inst_wallet(), &wallet_config, &g)
|
||||||
}
|
}
|
||||||
("web", Some(_)) => command::owner_api(inst_wallet(), &global_wallet_args),
|
("web", Some(_)) => command::owner_api(inst_wallet(), &wallet_config, &global_wallet_args),
|
||||||
("account", Some(args)) => {
|
("account", Some(args)) => {
|
||||||
let a = arg_parse!(parse_account_args(&args));
|
let a = arg_parse!(parse_account_args(&args));
|
||||||
command::account(inst_wallet(), a)
|
command::account(inst_wallet(), a)
|
||||||
|
|
|
@ -127,6 +127,7 @@ pub fn listen(config: &WalletConfig, args: &ListenArgs, g_args: &GlobalArgs) ->
|
||||||
|
|
||||||
pub fn owner_api(
|
pub fn owner_api(
|
||||||
wallet: Arc<Mutex<WalletInst<impl NodeClient + 'static, keychain::ExtKeychain>>>,
|
wallet: Arc<Mutex<WalletInst<impl NodeClient + 'static, keychain::ExtKeychain>>>,
|
||||||
|
config: &WalletConfig,
|
||||||
g_args: &GlobalArgs,
|
g_args: &GlobalArgs,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let res = controller::owner_listener(
|
let res = controller::owner_listener(
|
||||||
|
@ -134,6 +135,7 @@ pub fn owner_api(
|
||||||
"127.0.0.1:13420",
|
"127.0.0.1:13420",
|
||||||
g_args.node_api_secret.clone(),
|
g_args.node_api_secret.clone(),
|
||||||
g_args.tls_conf.clone(),
|
g_args.tls_conf.clone(),
|
||||||
|
config.owner_api_include_foreign.clone(),
|
||||||
);
|
);
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
return Err(ErrorKind::LibWallet(e.kind(), e.cause_string()).into());
|
return Err(ErrorKind::LibWallet(e.kind(), e.cause_string()).into());
|
||||||
|
|
|
@ -74,6 +74,7 @@ pub fn owner_listener<T: ?Sized, C, K>(
|
||||||
addr: &str,
|
addr: &str,
|
||||||
api_secret: Option<String>,
|
api_secret: Option<String>,
|
||||||
tls_config: Option<TLSConfig>,
|
tls_config: Option<TLSConfig>,
|
||||||
|
owner_api_include_foreign: Option<bool>,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||||
|
@ -81,7 +82,7 @@ where
|
||||||
C: NodeClient + 'static,
|
C: NodeClient + 'static,
|
||||||
K: Keychain + 'static,
|
K: Keychain + 'static,
|
||||||
{
|
{
|
||||||
let api_handler = OwnerAPIHandler::new(wallet);
|
let api_handler = OwnerAPIHandler::new(wallet.clone());
|
||||||
|
|
||||||
let mut router = Router::new();
|
let mut router = Router::new();
|
||||||
if api_secret.is_some() {
|
if api_secret.is_some() {
|
||||||
|
@ -95,6 +96,15 @@ where
|
||||||
.add_route("/v1/wallet/owner/**", Arc::new(api_handler))
|
.add_route("/v1/wallet/owner/**", Arc::new(api_handler))
|
||||||
.map_err(|_| ErrorKind::GenericError("Router failed to add route".to_string()))?;
|
.map_err(|_| ErrorKind::GenericError("Router failed to add route".to_string()))?;
|
||||||
|
|
||||||
|
// If so configured, add the foreign API to the same port
|
||||||
|
if owner_api_include_foreign.unwrap_or(false) {
|
||||||
|
info!("Starting HTTP Foreign API on Owner server at {}.", addr);
|
||||||
|
let foreign_api_handler = ForeignAPIHandler::new(wallet.clone());
|
||||||
|
router
|
||||||
|
.add_route("/v1/wallet/foreign/**", Arc::new(foreign_api_handler))
|
||||||
|
.map_err(|_| ErrorKind::GenericError("Router failed to add route".to_string()))?;
|
||||||
|
}
|
||||||
|
|
||||||
let mut apis = ApiServer::new();
|
let mut apis = ApiServer::new();
|
||||||
info!("Starting HTTP Owner API server at {}.", addr);
|
info!("Starting HTTP Owner API server at {}.", addr);
|
||||||
let socket_addr: SocketAddr = addr.parse().expect("unable to parse socket address");
|
let socket_addr: SocketAddr = addr.parse().expect("unable to parse socket address");
|
||||||
|
|
|
@ -48,6 +48,8 @@ pub struct WalletConfig {
|
||||||
// The api address of a running server node against which transaction inputs
|
// The api address of a running server node against which transaction inputs
|
||||||
// will be checked during send
|
// will be checked during send
|
||||||
pub check_node_api_http_addr: String,
|
pub check_node_api_http_addr: String,
|
||||||
|
// Whether to include foreign API endpoints on the Owner API
|
||||||
|
pub owner_api_include_foreign: Option<bool>,
|
||||||
// The directory in which wallet files are stored
|
// The directory in which wallet files are stored
|
||||||
pub data_file_dir: String,
|
pub data_file_dir: String,
|
||||||
/// TLS certificate file
|
/// TLS certificate file
|
||||||
|
@ -70,6 +72,7 @@ impl Default for WalletConfig {
|
||||||
api_secret_path: Some(".api_secret".to_string()),
|
api_secret_path: Some(".api_secret".to_string()),
|
||||||
node_api_secret_path: Some(".api_secret".to_string()),
|
node_api_secret_path: Some(".api_secret".to_string()),
|
||||||
check_node_api_http_addr: "http://127.0.0.1:3413".to_string(),
|
check_node_api_http_addr: "http://127.0.0.1:3413".to_string(),
|
||||||
|
owner_api_include_foreign: Some(false),
|
||||||
data_file_dir: ".".to_string(),
|
data_file_dir: ".".to_string(),
|
||||||
tls_certificate_file: None,
|
tls_certificate_file: None,
|
||||||
tls_certificate_key: None,
|
tls_certificate_key: None,
|
||||||
|
|
Loading…
Reference in a new issue