mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 03:21:08 +03:00
Implement Basic Auth for API and Owner API (#1566)
* Add api_secret * Add to base64 method * Add basic auth in API * Add Basic Auth to owner API * Add flag to enable disable basic auth * Add .api_secret file
This commit is contained in:
parent
acec59e249
commit
62fd8f2124
29 changed files with 427 additions and 228 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -718,6 +718,7 @@ dependencies = [
|
||||||
"grin_util 0.3.0",
|
"grin_util 0.3.0",
|
||||||
"grin_wallet 0.3.0",
|
"grin_wallet 0.3.0",
|
||||||
"pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -862,6 +863,7 @@ name = "grin_util"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
72
api/src/auth.rs
Normal file
72
api/src/auth.rs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
// Copyright 2018 The Grin Developers
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
use futures::future::ok;
|
||||||
|
use hyper::header::{HeaderValue, AUTHORIZATION, WWW_AUTHENTICATE};
|
||||||
|
use hyper::{Body, Request, Response, StatusCode};
|
||||||
|
use router::{Handler, HandlerObj, ResponseFuture};
|
||||||
|
|
||||||
|
// Basic Authentication Middleware
|
||||||
|
pub struct BasicAuthMiddleware {
|
||||||
|
api_basic_auth: String,
|
||||||
|
basic_realm: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BasicAuthMiddleware {
|
||||||
|
pub fn new(api_basic_auth: String, basic_realm: String) -> BasicAuthMiddleware {
|
||||||
|
BasicAuthMiddleware {
|
||||||
|
api_basic_auth,
|
||||||
|
basic_realm,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler for BasicAuthMiddleware {
|
||||||
|
fn call(
|
||||||
|
&self,
|
||||||
|
req: Request<Body>,
|
||||||
|
mut handlers: Box<Iterator<Item = HandlerObj>>,
|
||||||
|
) -> ResponseFuture {
|
||||||
|
if req.headers().contains_key(AUTHORIZATION) {
|
||||||
|
if req.headers()[AUTHORIZATION] == self.api_basic_auth {
|
||||||
|
handlers.next().unwrap().call(req, handlers)
|
||||||
|
} else {
|
||||||
|
// Forbidden 403
|
||||||
|
forbidden_response()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Unauthorized 401
|
||||||
|
unauthorized_response(&self.basic_realm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unauthorized_response(basic_realm: &str) -> ResponseFuture {
|
||||||
|
let response = Response::builder()
|
||||||
|
.status(StatusCode::UNAUTHORIZED)
|
||||||
|
.header(
|
||||||
|
WWW_AUTHENTICATE,
|
||||||
|
HeaderValue::from_str(basic_realm).unwrap(),
|
||||||
|
).body(Body::empty())
|
||||||
|
.unwrap();
|
||||||
|
Box::new(ok(response))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn forbidden_response() -> ResponseFuture {
|
||||||
|
let response = Response::builder()
|
||||||
|
.status(StatusCode::FORBIDDEN)
|
||||||
|
.body(Body::empty())
|
||||||
|
.unwrap();
|
||||||
|
Box::new(ok(response))
|
||||||
|
}
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
use failure::{Fail, ResultExt};
|
use failure::{Fail, ResultExt};
|
||||||
use http::uri::{InvalidUri, Uri};
|
use http::uri::{InvalidUri, Uri};
|
||||||
use hyper::header::{ACCEPT, USER_AGENT};
|
use hyper::header::{ACCEPT, AUTHORIZATION, USER_AGENT};
|
||||||
use hyper::rt::{Future, Stream};
|
use hyper::rt::{Future, Stream};
|
||||||
use hyper::{Body, Client, Request};
|
use hyper::{Body, Client, Request};
|
||||||
use hyper_tls;
|
use hyper_tls;
|
||||||
|
@ -27,27 +27,28 @@ use futures::future::{err, ok, Either};
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
use rest::{Error, ErrorKind};
|
use rest::{Error, ErrorKind};
|
||||||
|
use util::to_base64;
|
||||||
|
|
||||||
pub type ClientResponseFuture<T> = Box<Future<Item = T, Error = Error> + Send>;
|
pub type ClientResponseFuture<T> = Box<Future<Item = T, Error = Error> + Send>;
|
||||||
|
|
||||||
/// Helper function to easily issue a HTTP GET request against a given URL that
|
/// Helper function to easily issue a HTTP GET request against a given URL that
|
||||||
/// returns a JSON object. Handles request building, JSON deserialization and
|
/// returns a JSON object. Handles request building, JSON deserialization and
|
||||||
/// response code checking.
|
/// response code checking.
|
||||||
pub fn get<'a, T>(url: &'a str) -> Result<T, Error>
|
pub fn get<'a, T>(url: &'a str, api_secret: Option<String>) -> Result<T, Error>
|
||||||
where
|
where
|
||||||
for<'de> T: Deserialize<'de>,
|
for<'de> T: Deserialize<'de>,
|
||||||
{
|
{
|
||||||
handle_request(build_request(url, "GET", None)?)
|
handle_request(build_request(url, "GET", api_secret, None)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to easily issue an async HTTP GET request against a given
|
/// Helper function to easily issue an async HTTP GET request against a given
|
||||||
/// URL that returns a future. Handles request building, JSON deserialization
|
/// URL that returns a future. Handles request building, JSON deserialization
|
||||||
/// and response code checking.
|
/// and response code checking.
|
||||||
pub fn get_async<'a, T>(url: &'a str) -> ClientResponseFuture<T>
|
pub fn get_async<'a, T>(url: &'a str, api_secret: Option<String>) -> ClientResponseFuture<T>
|
||||||
where
|
where
|
||||||
for<'de> T: Deserialize<'de> + Send + 'static,
|
for<'de> T: Deserialize<'de> + Send + 'static,
|
||||||
{
|
{
|
||||||
match build_request(url, "GET", None) {
|
match build_request(url, "GET", api_secret, None) {
|
||||||
Ok(req) => Box::new(handle_request_async(req)),
|
Ok(req) => Box::new(handle_request_async(req)),
|
||||||
Err(e) => Box::new(err(e)),
|
Err(e) => Box::new(err(e)),
|
||||||
}
|
}
|
||||||
|
@ -57,12 +58,12 @@ where
|
||||||
/// object as body on a given URL that returns a JSON object. Handles request
|
/// object as body on a given URL that returns a JSON object. Handles request
|
||||||
/// building, JSON serialization and deserialization, and response code
|
/// building, JSON serialization and deserialization, and response code
|
||||||
/// checking.
|
/// checking.
|
||||||
pub fn post<IN, OUT>(url: &str, input: &IN) -> Result<OUT, Error>
|
pub fn post<IN, OUT>(url: &str, api_secret: Option<String>, input: &IN) -> Result<OUT, Error>
|
||||||
where
|
where
|
||||||
IN: Serialize,
|
IN: Serialize,
|
||||||
for<'de> OUT: Deserialize<'de>,
|
for<'de> OUT: Deserialize<'de>,
|
||||||
{
|
{
|
||||||
let req = create_post_request(url, input)?;
|
let req = create_post_request(url, api_secret, input)?;
|
||||||
handle_request(req)
|
handle_request(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,13 +71,17 @@ where
|
||||||
/// provided JSON object as body on a given URL that returns a future. Handles
|
/// provided JSON object as body on a given URL that returns a future. Handles
|
||||||
/// request building, JSON serialization and deserialization, and response code
|
/// request building, JSON serialization and deserialization, and response code
|
||||||
/// checking.
|
/// checking.
|
||||||
pub fn post_async<IN, OUT>(url: &str, input: &IN) -> ClientResponseFuture<OUT>
|
pub fn post_async<IN, OUT>(
|
||||||
|
url: &str,
|
||||||
|
input: &IN,
|
||||||
|
api_secret: Option<String>,
|
||||||
|
) -> ClientResponseFuture<OUT>
|
||||||
where
|
where
|
||||||
IN: Serialize,
|
IN: Serialize,
|
||||||
OUT: Send + 'static,
|
OUT: Send + 'static,
|
||||||
for<'de> OUT: Deserialize<'de>,
|
for<'de> OUT: Deserialize<'de>,
|
||||||
{
|
{
|
||||||
match create_post_request(url, input) {
|
match create_post_request(url, api_secret, input) {
|
||||||
Ok(req) => Box::new(handle_request_async(req)),
|
Ok(req) => Box::new(handle_request_async(req)),
|
||||||
Err(e) => Box::new(err(e)),
|
Err(e) => Box::new(err(e)),
|
||||||
}
|
}
|
||||||
|
@ -86,11 +91,11 @@ where
|
||||||
/// object as body on a given URL that returns nothing. Handles request
|
/// object as body on a given URL that returns nothing. Handles request
|
||||||
/// building, JSON serialization, and response code
|
/// building, JSON serialization, and response code
|
||||||
/// checking.
|
/// checking.
|
||||||
pub fn post_no_ret<IN>(url: &str, input: &IN) -> Result<(), Error>
|
pub fn post_no_ret<IN>(url: &str, api_secret: Option<String>, input: &IN) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
IN: Serialize,
|
IN: Serialize,
|
||||||
{
|
{
|
||||||
let req = create_post_request(url, input)?;
|
let req = create_post_request(url, api_secret, input)?;
|
||||||
send_request(req)?;
|
send_request(req)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -99,11 +104,15 @@ where
|
||||||
/// provided JSON object as body on a given URL that returns a future. Handles
|
/// provided JSON object as body on a given URL that returns a future. Handles
|
||||||
/// request building, JSON serialization and deserialization, and response code
|
/// request building, JSON serialization and deserialization, and response code
|
||||||
/// checking.
|
/// checking.
|
||||||
pub fn post_no_ret_async<IN>(url: &str, input: &IN) -> ClientResponseFuture<()>
|
pub fn post_no_ret_async<IN>(
|
||||||
|
url: &str,
|
||||||
|
api_secret: Option<String>,
|
||||||
|
input: &IN,
|
||||||
|
) -> ClientResponseFuture<()>
|
||||||
where
|
where
|
||||||
IN: Serialize,
|
IN: Serialize,
|
||||||
{
|
{
|
||||||
match create_post_request(url, input) {
|
match create_post_request(url, api_secret, input) {
|
||||||
Ok(req) => Box::new(send_request_async(req).and_then(|_| ok(()))),
|
Ok(req) => Box::new(send_request_async(req).and_then(|_| ok(()))),
|
||||||
Err(e) => Box::new(err(e)),
|
Err(e) => Box::new(err(e)),
|
||||||
}
|
}
|
||||||
|
@ -112,13 +121,21 @@ where
|
||||||
fn build_request<'a>(
|
fn build_request<'a>(
|
||||||
url: &'a str,
|
url: &'a str,
|
||||||
method: &str,
|
method: &str,
|
||||||
|
api_secret: Option<String>,
|
||||||
body: Option<String>,
|
body: Option<String>,
|
||||||
) -> Result<Request<Body>, Error> {
|
) -> Result<Request<Body>, Error> {
|
||||||
let uri = url.parse::<Uri>().map_err::<Error, _>(|e: InvalidUri| {
|
let uri = url.parse::<Uri>().map_err::<Error, _>(|e: InvalidUri| {
|
||||||
e.context(ErrorKind::Argument(format!("Invalid url {}", url)))
|
e.context(ErrorKind::Argument(format!("Invalid url {}", url)))
|
||||||
.into()
|
.into()
|
||||||
})?;
|
})?;
|
||||||
Request::builder()
|
let mut builder = Request::builder();
|
||||||
|
if api_secret.is_some() {
|
||||||
|
let basic_auth =
|
||||||
|
"Basic ".to_string() + &to_base64(&("grin:".to_string() + &api_secret.unwrap()));
|
||||||
|
builder.header(AUTHORIZATION, basic_auth);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder
|
||||||
.method(method)
|
.method(method)
|
||||||
.uri(uri)
|
.uri(uri)
|
||||||
.header(USER_AGENT, "grin-client")
|
.header(USER_AGENT, "grin-client")
|
||||||
|
@ -126,20 +143,23 @@ fn build_request<'a>(
|
||||||
.body(match body {
|
.body(match body {
|
||||||
None => Body::empty(),
|
None => Body::empty(),
|
||||||
Some(json) => json.into(),
|
Some(json) => json.into(),
|
||||||
})
|
}).map_err(|e| {
|
||||||
.map_err(|e| {
|
|
||||||
ErrorKind::RequestError(format!("Bad request {} {}: {}", method, url, e)).into()
|
ErrorKind::RequestError(format!("Bad request {} {}: {}", method, url, e)).into()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_post_request<IN>(url: &str, input: &IN) -> Result<Request<Body>, Error>
|
fn create_post_request<IN>(
|
||||||
|
url: &str,
|
||||||
|
api_secret: Option<String>,
|
||||||
|
input: &IN,
|
||||||
|
) -> Result<Request<Body>, Error>
|
||||||
where
|
where
|
||||||
IN: Serialize,
|
IN: Serialize,
|
||||||
{
|
{
|
||||||
let json = serde_json::to_string(input).context(ErrorKind::Internal(
|
let json = serde_json::to_string(input).context(ErrorKind::Internal(
|
||||||
"Could not serialize data to JSON".to_owned(),
|
"Could not serialize data to JSON".to_owned(),
|
||||||
))?;
|
))?;
|
||||||
build_request(url, "POST", Some(json))
|
build_request(url, "POST", api_secret, Some(json))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_request<T>(req: Request<Body>) -> Result<T, Error>
|
fn handle_request<T>(req: Request<Body>) -> Result<T, Error>
|
||||||
|
@ -183,8 +203,7 @@ fn send_request_async(req: Request<Body>) -> Box<Future<Item = String, Error = E
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ErrorKind::RequestError(format!("Cannot read response body: {}", e))
|
ErrorKind::RequestError(format!("Cannot read response body: {}", e))
|
||||||
.into()
|
.into()
|
||||||
})
|
}).concat2()
|
||||||
.concat2()
|
|
||||||
.and_then(|ch| ok(String::from_utf8_lossy(&ch.to_vec()).to_string())),
|
.and_then(|ch| ok(String::from_utf8_lossy(&ch.to_vec()).to_string())),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ use futures::future::ok;
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use hyper::{Body, Request, StatusCode};
|
use hyper::{Body, Request, StatusCode};
|
||||||
|
|
||||||
|
use auth::BasicAuthMiddleware;
|
||||||
use chain;
|
use chain;
|
||||||
use core::core::hash::{Hash, Hashed};
|
use core::core::hash::{Hash, Hashed};
|
||||||
use core::core::{OutputFeatures, OutputIdentifier, Transaction};
|
use core::core::{OutputFeatures, OutputIdentifier, Transaction};
|
||||||
|
@ -795,10 +796,17 @@ pub fn start_rest_apis(
|
||||||
chain: Weak<chain::Chain>,
|
chain: Weak<chain::Chain>,
|
||||||
tx_pool: Weak<RwLock<pool::TransactionPool>>,
|
tx_pool: Weak<RwLock<pool::TransactionPool>>,
|
||||||
peers: Weak<p2p::Peers>,
|
peers: Weak<p2p::Peers>,
|
||||||
|
api_secret: Option<String>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut apis = ApiServer::new();
|
let mut apis = ApiServer::new();
|
||||||
|
let mut router = build_router(chain, tx_pool, peers).expect("unable to build API router");
|
||||||
let router = build_router(chain, tx_pool, peers).expect("unable to build API router");
|
if api_secret.is_some() {
|
||||||
|
let api_basic_auth =
|
||||||
|
"Basic ".to_string() + &util::to_base64(&("grin:".to_string() + &api_secret.unwrap()));
|
||||||
|
let basic_realm = "Basic realm=GrinAPI".to_string();
|
||||||
|
let basic_auth_middleware = Arc::new(BasicAuthMiddleware::new(api_basic_auth, basic_realm));
|
||||||
|
router.add_middleware(basic_auth_middleware);
|
||||||
|
}
|
||||||
|
|
||||||
info!(LOGGER, "Starting HTTP API server at {}.", addr);
|
info!(LOGGER, "Starting HTTP 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");
|
||||||
|
@ -875,7 +883,7 @@ pub fn build_router(
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut router = Router::new();
|
let mut router = Router::new();
|
||||||
// example how we can use midlleware
|
|
||||||
router.add_route("/v1/", Arc::new(index_handler))?;
|
router.add_route("/v1/", Arc::new(index_handler))?;
|
||||||
router.add_route("/v1/blocks/*", Arc::new(block_handler))?;
|
router.add_route("/v1/blocks/*", Arc::new(block_handler))?;
|
||||||
router.add_route("/v1/headers/*", Arc::new(header_handler))?;
|
router.add_route("/v1/headers/*", Arc::new(header_handler))?;
|
||||||
|
|
|
@ -41,6 +41,7 @@ extern crate tokio;
|
||||||
extern crate tokio_core;
|
extern crate tokio_core;
|
||||||
extern crate tokio_tls;
|
extern crate tokio_tls;
|
||||||
|
|
||||||
|
pub mod auth;
|
||||||
pub mod client;
|
pub mod client;
|
||||||
mod handlers;
|
mod handlers;
|
||||||
mod rest;
|
mod rest;
|
||||||
|
@ -48,6 +49,7 @@ mod router;
|
||||||
mod types;
|
mod types;
|
||||||
mod web;
|
mod web;
|
||||||
|
|
||||||
|
pub use auth::BasicAuthMiddleware;
|
||||||
pub use handlers::start_rest_apis;
|
pub use handlers::start_rest_apis;
|
||||||
pub use rest::*;
|
pub use rest::*;
|
||||||
pub use router::*;
|
pub use router::*;
|
||||||
|
|
|
@ -132,8 +132,7 @@ impl ApiServer {
|
||||||
.map_err(|e| eprintln!("HTTP API server error: {}", e));
|
.map_err(|e| eprintln!("HTTP API server error: {}", e));
|
||||||
|
|
||||||
rt::run(server);
|
rt::run(server);
|
||||||
})
|
}).map_err(|_| ErrorKind::Internal("failed to spawn API thread".to_string()).into())
|
||||||
.map_err(|_| ErrorKind::Internal("failed to spawn API thread".to_string()).into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Starts the TLS ApiServer at the provided address.
|
/// Starts the TLS ApiServer at the provided address.
|
||||||
|
@ -165,15 +164,13 @@ impl ApiServer {
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
|
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
|
||||||
}),
|
}),
|
||||||
router,
|
router,
|
||||||
)
|
).then(|res| match res {
|
||||||
.then(|res| match res {
|
|
||||||
Ok(conn) => Ok(Some(conn)),
|
Ok(conn) => Ok(Some(conn)),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Error: {}", e);
|
eprintln!("Error: {}", e);
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
})
|
}).for_each(|conn_opt| {
|
||||||
.for_each(|conn_opt| {
|
|
||||||
if let Some(conn) = conn_opt {
|
if let Some(conn) = conn_opt {
|
||||||
rt::spawn(
|
rt::spawn(
|
||||||
conn.and_then(|c| c.map_err(|e| panic!("Hyper error {}", e)))
|
conn.and_then(|c| c.map_err(|e| panic!("Hyper error {}", e)))
|
||||||
|
@ -184,8 +181,7 @@ impl ApiServer {
|
||||||
});
|
});
|
||||||
|
|
||||||
rt::run(server);
|
rt::run(server);
|
||||||
})
|
}).map_err(|_| ErrorKind::Internal("failed to spawn API thread".to_string()).into())
|
||||||
.map_err(|_| ErrorKind::Internal("failed to spawn API thread".to_string()).into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stops the API server, it panics in case of error
|
/// Stops the API server, it panics in case of error
|
||||||
|
|
|
@ -1,3 +1,17 @@
|
||||||
|
// Copyright 2018 The Grin Developers
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
use futures::future;
|
use futures::future;
|
||||||
use hyper;
|
use hyper;
|
||||||
use hyper::rt::Future;
|
use hyper::rt::Future;
|
||||||
|
@ -133,8 +147,7 @@ impl Router {
|
||||||
.find(|&id| {
|
.find(|&id| {
|
||||||
let node_key = self.node(*id).key;
|
let node_key = self.node(*id).key;
|
||||||
node_key == key || node_key == *WILDCARD_HASH || node_key == *WILDCARD_STOP_HASH
|
node_key == key || node_key == *WILDCARD_HASH || node_key == *WILDCARD_STOP_HASH
|
||||||
})
|
}).cloned()
|
||||||
.cloned()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_empty_node(&mut self, parent: NodeId, key: u64) -> NodeId {
|
fn add_empty_node(&mut self, parent: NodeId, key: u64) -> NodeId {
|
||||||
|
|
|
@ -280,8 +280,8 @@ impl OutputPrintable {
|
||||||
let mut merkle_proof = None;
|
let mut merkle_proof = None;
|
||||||
if output
|
if output
|
||||||
.features
|
.features
|
||||||
.contains(core::transaction::OutputFeatures::COINBASE_OUTPUT) && !spent
|
.contains(core::transaction::OutputFeatures::COINBASE_OUTPUT)
|
||||||
&& block_header.is_some()
|
&& !spent && block_header.is_some()
|
||||||
{
|
{
|
||||||
merkle_proof = chain.get_merkle_proof(&out_id, &block_header.unwrap()).ok()
|
merkle_proof = chain.get_merkle_proof(&out_id, &block_header.unwrap()).ok()
|
||||||
};
|
};
|
||||||
|
@ -564,8 +564,7 @@ impl BlockPrintable {
|
||||||
Some(&block.header),
|
Some(&block.header),
|
||||||
include_proof,
|
include_proof,
|
||||||
)
|
)
|
||||||
})
|
}).collect();
|
||||||
.collect();
|
|
||||||
let kernels = block
|
let kernels = block
|
||||||
.kernels()
|
.kernels()
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
@ -71,7 +71,7 @@ fn test_start_api() {
|
||||||
let addr: SocketAddr = server_addr.parse().expect("unable to parse server address");
|
let addr: SocketAddr = server_addr.parse().expect("unable to parse server address");
|
||||||
assert!(server.start(addr, router).is_ok());
|
assert!(server.start(addr, router).is_ok());
|
||||||
let url = format!("http://{}/v1/", server_addr);
|
let url = format!("http://{}/v1/", server_addr);
|
||||||
let index = api::client::get::<Vec<String>>(url.as_str()).unwrap();
|
let index = api::client::get::<Vec<String>>(url.as_str(), None).unwrap();
|
||||||
assert_eq!(index.len(), 2);
|
assert_eq!(index.len(), 2);
|
||||||
assert_eq!(counter.value(), 1);
|
assert_eq!(counter.value(), 1);
|
||||||
assert!(server.stop());
|
assert!(server.stop());
|
||||||
|
@ -96,7 +96,7 @@ fn test_start_api_tls() {
|
||||||
let addr: SocketAddr = server_addr.parse().expect("unable to parse server address");
|
let addr: SocketAddr = server_addr.parse().expect("unable to parse server address");
|
||||||
assert!(server.start_tls(addr, router, tls_conf).is_ok());
|
assert!(server.start_tls(addr, router, tls_conf).is_ok());
|
||||||
let url = format!("https://{}/v1/", server_addr);
|
let url = format!("https://{}/v1/", server_addr);
|
||||||
let index = api::client::get::<Vec<String>>(url.as_str()).unwrap();
|
let index = api::client::get::<Vec<String>>(url.as_str(), None).unwrap();
|
||||||
assert_eq!(index.len(), 2);
|
assert_eq!(index.len(), 2);
|
||||||
assert!(!server.stop());
|
assert!(!server.stop());
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ workspace = ".."
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
rand = "0.5"
|
||||||
serde = "1"
|
serde = "1"
|
||||||
serde_derive = "1"
|
serde_derive = "1"
|
||||||
toml = "0.4"
|
toml = "0.4"
|
||||||
|
|
|
@ -36,16 +36,22 @@ fn comments() -> HashMap<String, String> {
|
||||||
#########################################
|
#########################################
|
||||||
|
|
||||||
#Server connection details
|
#Server connection details
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"api_http_addr".to_string(),
|
"api_http_addr".to_string(),
|
||||||
"
|
"
|
||||||
#the address on which services will listen, e.g. Transaction Pool
|
#the address on which services will listen, e.g. Transaction Pool
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
);
|
||||||
|
|
||||||
|
retval.insert(
|
||||||
|
"api_secret_path".to_string(),
|
||||||
|
"
|
||||||
|
#path of the secret token used by the API to authenticate the calls
|
||||||
|
#comment the it to disable basic auth
|
||||||
|
".to_string(),
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
|
@ -53,8 +59,7 @@ fn comments() -> HashMap<String, String> {
|
||||||
"
|
"
|
||||||
#the directory, relative to current, in which the grin blockchain
|
#the directory, relative to current, in which the grin blockchain
|
||||||
#is stored
|
#is stored
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
|
@ -66,35 +71,31 @@ fn comments() -> HashMap<String, String> {
|
||||||
#UserTesting - For regular user testing (cuckoo 16)
|
#UserTesting - For regular user testing (cuckoo 16)
|
||||||
#Testnet1 - Testnet1 genesis block (cuckoo 16)
|
#Testnet1 - Testnet1 genesis block (cuckoo 16)
|
||||||
#Testnet2 - Testnet2 genesis block (cuckoo 30)
|
#Testnet2 - Testnet2 genesis block (cuckoo 30)
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"chain_validation_mode".to_string(),
|
"chain_validation_mode".to_string(),
|
||||||
"
|
"
|
||||||
#The chain validation mode, defines how often (if at all) we
|
#the chain validation mode, defines how often (if at all) we
|
||||||
#want to run a full chain validation. Can be:
|
#want to run a full chain validation. Can be:
|
||||||
#\"EveryBlock\" - run full chain validation when processing each block (except during sync)
|
#\"EveryBlock\" - run full chain validation when processing each block (except during sync)
|
||||||
#\"Disabled\" - disable full chain validation (just run regular block validation)
|
#\"Disabled\" - disable full chain validation (just run regular block validation)
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"archive_mode".to_string(),
|
"archive_mode".to_string(),
|
||||||
"
|
"
|
||||||
#run the node in \"full archive\" mode (default is fast-sync, pruned node)
|
#run the node in \"full archive\" mode (default is fast-sync, pruned node)
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"skip_sync_wait".to_string(),
|
"skip_sync_wait".to_string(),
|
||||||
"
|
"
|
||||||
#skip waiting for sync on startup, (optional param, mostly for testing)
|
#skip waiting for sync on startup, (optional param, mostly for testing)
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
|
@ -102,8 +103,7 @@ fn comments() -> HashMap<String, String> {
|
||||||
"
|
"
|
||||||
#whether to run the ncurses TUI. Ncurses must be installed and this
|
#whether to run the ncurses TUI. Ncurses must be installed and this
|
||||||
#will also disable logging to stdout
|
#will also disable logging to stdout
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
|
@ -112,8 +112,7 @@ fn comments() -> HashMap<String, String> {
|
||||||
#Whether to run a test miner. This is only for developer testing (chaintype
|
#Whether to run a test miner. This is only for developer testing (chaintype
|
||||||
#usertesting) at cuckoo 16, and will only mine into the default wallet port.
|
#usertesting) at cuckoo 16, and will only mine into the default wallet port.
|
||||||
#real mining should use the standalone grin-miner
|
#real mining should use the standalone grin-miner
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
|
@ -122,39 +121,34 @@ fn comments() -> HashMap<String, String> {
|
||||||
#########################################
|
#########################################
|
||||||
### DANDELION CONFIGURATION ###
|
### DANDELION CONFIGURATION ###
|
||||||
#########################################
|
#########################################
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"relay_secs".to_string(),
|
"relay_secs".to_string(),
|
||||||
"
|
"
|
||||||
#dandelion relay time (choose new relay peer every n secs)
|
#dandelion relay time (choose new relay peer every n secs)
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"embargo_secs".to_string(),
|
"embargo_secs".to_string(),
|
||||||
"
|
"
|
||||||
#fluff and broadcast after embargo expires if tx not seen on network
|
#fluff and broadcast after embargo expires if tx not seen on network
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"patience_secs".to_string(),
|
"patience_secs".to_string(),
|
||||||
"
|
"
|
||||||
#run dandelion stem/fluff processing every n secs (stem tx aggregation in this window)
|
#run dandelion stem/fluff processing every n secs (stem tx aggregation in this window)
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"stem_probability".to_string(),
|
"stem_probability".to_string(),
|
||||||
"
|
"
|
||||||
#dandelion stem probability (stem 90% of the time, fluff 10% of the time)
|
#dandelion stem probability (stem 90% of the time, fluff 10% of the time)
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
|
@ -166,26 +160,23 @@ fn comments() -> HashMap<String, String> {
|
||||||
### SERVER P2P CONFIGURATION ###
|
### SERVER P2P CONFIGURATION ###
|
||||||
#########################################
|
#########################################
|
||||||
#The P2P server details (i.e. the server that communicates with other
|
#The P2P server details (i.e. the server that communicates with other
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"host".to_string(),
|
"host".to_string(),
|
||||||
"
|
"
|
||||||
#The interface on which to listen.
|
#The interface on which to listen.
|
||||||
#0.0.0.0 will listen on all interfaces, alowing others to interact
|
#0.0.0.0 will listen on all interfaces, allowing others to interact
|
||||||
#127.0.0.1 will listen on the local machine only
|
#127.0.0.1 will listen on the local machine only
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"port".to_string(),
|
"port".to_string(),
|
||||||
"
|
"
|
||||||
#The port on which to listen.
|
#The port on which to listen.
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
|
@ -213,17 +204,15 @@ fn comments() -> HashMap<String, String> {
|
||||||
#until we get to at least this number
|
#until we get to at least this number
|
||||||
#peer_min_preferred_count = 8
|
#peer_min_preferred_count = 8
|
||||||
|
|
||||||
#How to seed this server, can be None, List or DNSSeed
|
#how to seed this server, can be None, List or DNSSeed
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"[server.p2p_config.capabilities]".to_string(),
|
"[server.p2p_config.capabilities]".to_string(),
|
||||||
"#7 = Bit flags for FULL_NODE, this structure needs to be changed
|
"#7 = Bit flags for FULL_NODE, this structure needs to be changed
|
||||||
#internally to make it more configurable
|
#internally to make it more configurable
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
|
@ -232,24 +221,21 @@ fn comments() -> HashMap<String, String> {
|
||||||
#########################################
|
#########################################
|
||||||
### MEMPOOL CONFIGURATION ###
|
### MEMPOOL CONFIGURATION ###
|
||||||
#########################################
|
#########################################
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"accept_fee_base".to_string(),
|
"accept_fee_base".to_string(),
|
||||||
"
|
"
|
||||||
#Base fee that's accepted into the pool
|
#base fee that's accepted into the pool
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"max_pool_size".to_string(),
|
"max_pool_size".to_string(),
|
||||||
"
|
"
|
||||||
#Maximum number of transactions allowed in the pool
|
#maximum number of transactions allowed in the pool
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
|
@ -258,57 +244,50 @@ fn comments() -> HashMap<String, String> {
|
||||||
################################################
|
################################################
|
||||||
### STRATUM MINING SERVER CONFIGURATION ###
|
### STRATUM MINING SERVER CONFIGURATION ###
|
||||||
################################################
|
################################################
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"enable_stratum_server".to_string(),
|
"enable_stratum_server".to_string(),
|
||||||
"
|
"
|
||||||
#whether stratum server is enabled
|
#whether stratum server is enabled
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"stratum_server_addr".to_string(),
|
"stratum_server_addr".to_string(),
|
||||||
"
|
"
|
||||||
#what port and address for the stratum server to listen on
|
#what port and address for the stratum server to listen on
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"attempt_time_per_block".to_string(),
|
"attempt_time_per_block".to_string(),
|
||||||
"
|
"
|
||||||
#The amount of time, in seconds, to attempt to mine on a particular
|
#the amount of time, in seconds, to attempt to mine on a particular
|
||||||
#header before stopping and re-collecting transactions from the pool
|
#header before stopping and re-collecting transactions from the pool
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"minimum_share_difficulty".to_string(),
|
"minimum_share_difficulty".to_string(),
|
||||||
"
|
"
|
||||||
#The minimum acceptable share difficulty to request from miners
|
#the minimum acceptable share difficulty to request from miners
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"wallet_listener_url".to_string(),
|
"wallet_listener_url".to_string(),
|
||||||
"
|
"
|
||||||
#the wallet receiver to which coinbase rewards will be sent
|
#the wallet receiver to which coinbase rewards will be sent
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"burn_reward".to_string(),
|
"burn_reward".to_string(),
|
||||||
"
|
"
|
||||||
#whether to ignore the reward (mostly for testing)
|
#whether to ignore the reward (mostly for testing)
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
|
@ -317,40 +296,42 @@ fn comments() -> HashMap<String, String> {
|
||||||
#########################################
|
#########################################
|
||||||
### WALLET CONFIGURATION ###
|
### WALLET CONFIGURATION ###
|
||||||
#########################################
|
#########################################
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"api_listen_interface".to_string(),
|
"api_listen_interface".to_string(),
|
||||||
"
|
"
|
||||||
# Host IP for wallet listener, change to \"0.0.0.0\" to receive grins
|
#host IP for wallet listener, change to \"0.0.0.0\" to receive grins
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"api_listen_port".to_string(),
|
"api_listen_port".to_string(),
|
||||||
"
|
"
|
||||||
# Port for wallet listener
|
#port for wallet listener
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
retval.insert(
|
||||||
|
"api_secret_path".to_string(),
|
||||||
|
"
|
||||||
|
#path of the secret token used by the API to authenticate the calls
|
||||||
|
#comment it to disable basic auth
|
||||||
|
".to_string(),
|
||||||
|
);
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"check_node_api_http_addr".to_string(),
|
"check_node_api_http_addr".to_string(),
|
||||||
"
|
"
|
||||||
# Where the wallet should find a running node
|
#where the wallet should find a running node
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"data_file_dir".to_string(),
|
"data_file_dir".to_string(),
|
||||||
"
|
"
|
||||||
# Where to find wallet files (seed, data, etc)
|
#where to find wallet files (seed, data, etc)
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
|
@ -359,56 +340,49 @@ fn comments() -> HashMap<String, String> {
|
||||||
#########################################
|
#########################################
|
||||||
### LOGGING CONFIGURATION ###
|
### LOGGING CONFIGURATION ###
|
||||||
#########################################
|
#########################################
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"log_to_stdout".to_string(),
|
"log_to_stdout".to_string(),
|
||||||
"
|
"
|
||||||
# Whether to log to stdout
|
#whether to log to stdout
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"stdout_log_level".to_string(),
|
"stdout_log_level".to_string(),
|
||||||
"
|
"
|
||||||
# Log level for stdout: Critical, Error, Warning, Info, Debug, Trace
|
#log level for stdout: Critical, Error, Warning, Info, Debug, Trace
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"log_to_file".to_string(),
|
"log_to_file".to_string(),
|
||||||
"
|
"
|
||||||
# Whether to log to a file
|
#whether to log to a file
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"file_log_level".to_string(),
|
"file_log_level".to_string(),
|
||||||
"
|
"
|
||||||
# Log level for file: Critical, Error, Warning, Info, Debug, Trace
|
#log level for file: Critical, Error, Warning, Info, Debug, Trace
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"log_file_path".to_string(),
|
"log_file_path".to_string(),
|
||||||
"
|
"
|
||||||
# Log file path
|
#log file path
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval.insert(
|
retval.insert(
|
||||||
"log_file_append".to_string(),
|
"log_file_append".to_string(),
|
||||||
"
|
"
|
||||||
# Whether to append to the log file (true), or replace it on every run (false)
|
#whether to append to the log file (true), or replace it on every run (false)
|
||||||
"
|
".to_string(),
|
||||||
.to_string(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
retval
|
retval
|
||||||
|
|
|
@ -15,9 +15,12 @@
|
||||||
//! Configuration file management
|
//! Configuration file management
|
||||||
|
|
||||||
use dirs;
|
use dirs;
|
||||||
|
use rand::distributions::{Alphanumeric, Distribution};
|
||||||
|
use rand::thread_rng;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
|
use std::io::BufReader;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use toml;
|
use toml;
|
||||||
|
@ -40,6 +43,7 @@ const WALLET_LOG_FILE_NAME: &'static str = "grin-wallet.log";
|
||||||
const GRIN_HOME: &'static str = ".grin";
|
const GRIN_HOME: &'static str = ".grin";
|
||||||
const GRIN_CHAIN_DIR: &'static str = "chain_data";
|
const GRIN_CHAIN_DIR: &'static str = "chain_data";
|
||||||
const GRIN_WALLET_DIR: &'static str = "wallet_data";
|
const GRIN_WALLET_DIR: &'static str = "wallet_data";
|
||||||
|
const API_SECRET_FILE_NAME: &'static str = ".api_secret";
|
||||||
|
|
||||||
fn get_grin_path() -> Result<PathBuf, ConfigError> {
|
fn get_grin_path() -> Result<PathBuf, ConfigError> {
|
||||||
// Check if grin dir exists
|
// Check if grin dir exists
|
||||||
|
@ -78,8 +82,45 @@ fn check_config_current_dir(path: &str) -> Option<PathBuf> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create file with api secret
|
||||||
|
fn init_api_secret(api_secret_path: &PathBuf) -> Result<(), ConfigError> {
|
||||||
|
let mut api_secret_file = File::create(api_secret_path)?;
|
||||||
|
let api_secret: String = Alphanumeric
|
||||||
|
.sample_iter(&mut thread_rng())
|
||||||
|
.take(20)
|
||||||
|
.collect();
|
||||||
|
api_secret_file.write_all(api_secret.as_bytes())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// // Check if file contains a secret and nothing else
|
||||||
|
fn check_api_secret(api_secret_path: &PathBuf) -> Result<(), ConfigError> {
|
||||||
|
let api_secret_file = File::open(api_secret_path)?;
|
||||||
|
let buf_reader = BufReader::new(api_secret_file);
|
||||||
|
let mut lines_iter = buf_reader.lines();
|
||||||
|
let first_line = lines_iter.next();
|
||||||
|
if first_line.is_none() || first_line.unwrap().is_err() {
|
||||||
|
fs::remove_file(api_secret_path)?;
|
||||||
|
init_api_secret(api_secret_path)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check that the api secret file exists and is valid
|
||||||
|
pub fn check_api_secret_file() -> Result<(), ConfigError> {
|
||||||
|
let grin_path = get_grin_path()?;
|
||||||
|
let mut api_secret_path = grin_path.clone();
|
||||||
|
api_secret_path.push(API_SECRET_FILE_NAME);
|
||||||
|
if !api_secret_path.exists() {
|
||||||
|
init_api_secret(&api_secret_path)
|
||||||
|
} else {
|
||||||
|
check_api_secret(&api_secret_path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Handles setup and detection of paths for node
|
/// Handles setup and detection of paths for node
|
||||||
pub fn initial_setup_server() -> Result<GlobalConfig, ConfigError> {
|
pub fn initial_setup_server() -> Result<GlobalConfig, ConfigError> {
|
||||||
|
check_api_secret_file()?;
|
||||||
// Use config file if current directory if it exists, .grin home otherwise
|
// Use config file if current directory if it exists, .grin home otherwise
|
||||||
if let Some(p) = check_config_current_dir(SERVER_CONFIG_FILE_NAME) {
|
if let Some(p) = check_config_current_dir(SERVER_CONFIG_FILE_NAME) {
|
||||||
GlobalConfig::new(p.to_str().unwrap())
|
GlobalConfig::new(p.to_str().unwrap())
|
||||||
|
@ -98,12 +139,14 @@ pub fn initial_setup_server() -> Result<GlobalConfig, ConfigError> {
|
||||||
default_config.update_paths(&grin_path);
|
default_config.update_paths(&grin_path);
|
||||||
default_config.write_to_file(config_path.to_str().unwrap())?;
|
default_config.write_to_file(config_path.to_str().unwrap())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalConfig::new(config_path.to_str().unwrap())
|
GlobalConfig::new(config_path.to_str().unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles setup and detection of paths for wallet
|
/// Handles setup and detection of paths for wallet
|
||||||
pub fn initial_setup_wallet() -> Result<GlobalWalletConfig, ConfigError> {
|
pub fn initial_setup_wallet() -> Result<GlobalWalletConfig, ConfigError> {
|
||||||
|
check_api_secret_file()?;
|
||||||
// Use config file if current directory if it exists, .grin home otherwise
|
// Use config file if current directory if it exists, .grin home otherwise
|
||||||
if let Some(p) = check_config_current_dir(WALLET_CONFIG_FILE_NAME) {
|
if let Some(p) = check_config_current_dir(WALLET_CONFIG_FILE_NAME) {
|
||||||
GlobalWalletConfig::new(p.to_str().unwrap())
|
GlobalWalletConfig::new(p.to_str().unwrap())
|
||||||
|
@ -122,6 +165,7 @@ pub fn initial_setup_wallet() -> Result<GlobalWalletConfig, ConfigError> {
|
||||||
default_config.update_paths(&grin_path);
|
default_config.update_paths(&grin_path);
|
||||||
default_config.write_to_file(config_path.to_str().unwrap())?;
|
default_config.write_to_file(config_path.to_str().unwrap())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalWalletConfig::new(config_path.to_str().unwrap())
|
GlobalWalletConfig::new(config_path.to_str().unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,6 +260,10 @@ impl GlobalConfig {
|
||||||
let mut chain_path = grin_home.clone();
|
let mut chain_path = grin_home.clone();
|
||||||
chain_path.push(GRIN_CHAIN_DIR);
|
chain_path.push(GRIN_CHAIN_DIR);
|
||||||
self.members.as_mut().unwrap().server.db_root = chain_path.to_str().unwrap().to_owned();
|
self.members.as_mut().unwrap().server.db_root = chain_path.to_str().unwrap().to_owned();
|
||||||
|
let mut secret_path = grin_home.clone();
|
||||||
|
secret_path.push(API_SECRET_FILE_NAME);
|
||||||
|
self.members.as_mut().unwrap().server.api_secret_path =
|
||||||
|
Some(secret_path.to_str().unwrap().to_owned());
|
||||||
let mut log_path = grin_home.clone();
|
let mut log_path = grin_home.clone();
|
||||||
log_path.push(SERVER_LOG_FILE_NAME);
|
log_path.push(SERVER_LOG_FILE_NAME);
|
||||||
self.members
|
self.members
|
||||||
|
@ -319,6 +367,10 @@ impl GlobalWalletConfig {
|
||||||
wallet_path.push(GRIN_WALLET_DIR);
|
wallet_path.push(GRIN_WALLET_DIR);
|
||||||
self.members.as_mut().unwrap().wallet.data_file_dir =
|
self.members.as_mut().unwrap().wallet.data_file_dir =
|
||||||
wallet_path.to_str().unwrap().to_owned();
|
wallet_path.to_str().unwrap().to_owned();
|
||||||
|
let mut secret_path = wallet_home.clone();
|
||||||
|
secret_path.push(API_SECRET_FILE_NAME);
|
||||||
|
self.members.as_mut().unwrap().wallet.api_secret_path =
|
||||||
|
Some(secret_path.to_str().unwrap().to_owned());
|
||||||
let mut log_path = wallet_home.clone();
|
let mut log_path = wallet_home.clone();
|
||||||
log_path.push(WALLET_LOG_FILE_NAME);
|
log_path.push(WALLET_LOG_FILE_NAME);
|
||||||
self.members
|
self.members
|
||||||
|
|
|
@ -20,9 +20,10 @@
|
||||||
#![deny(unused_mut)]
|
#![deny(unused_mut)]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
|
extern crate dirs;
|
||||||
|
extern crate rand;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
extern crate dirs;
|
|
||||||
extern crate toml;
|
extern crate toml;
|
||||||
|
|
||||||
extern crate grin_p2p as p2p;
|
extern crate grin_p2p as p2p;
|
||||||
|
@ -34,5 +35,5 @@ mod comments;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
pub use config::{initial_setup_server, initial_setup_wallet};
|
pub use config::{check_api_secret_file, initial_setup_server, initial_setup_wallet};
|
||||||
pub use types::{ConfigError, ConfigMembers, GlobalConfig, GlobalWalletConfig};
|
pub use types::{ConfigError, ConfigMembers, GlobalConfig, GlobalWalletConfig};
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
//! Server types
|
//! Server types
|
||||||
|
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
@ -113,6 +112,9 @@ pub struct ServerConfig {
|
||||||
/// Network address for the Rest API HTTP server.
|
/// Network address for the Rest API HTTP server.
|
||||||
pub api_http_addr: String,
|
pub api_http_addr: String,
|
||||||
|
|
||||||
|
/// Location of secret for basic auth on Rest API HTTP server.
|
||||||
|
pub api_secret_path: Option<String>,
|
||||||
|
|
||||||
/// Setup the server for tests, testnet or mainnet
|
/// Setup the server for tests, testnet or mainnet
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub chain_type: ChainTypes,
|
pub chain_type: ChainTypes,
|
||||||
|
@ -163,11 +165,10 @@ impl ServerConfig {
|
||||||
// check [server.p2p_config.capabilities] with 'archive_mode' in [server]
|
// check [server.p2p_config.capabilities] with 'archive_mode' in [server]
|
||||||
if let Some(archive) = self.archive_mode {
|
if let Some(archive) = self.archive_mode {
|
||||||
// note: slog not available before config loaded, only print here.
|
// note: slog not available before config loaded, only print here.
|
||||||
if archive
|
if archive != self
|
||||||
!= self
|
.p2p_config
|
||||||
.p2p_config
|
.capabilities
|
||||||
.capabilities
|
.contains(p2p::Capabilities::FULL_HIST)
|
||||||
.contains(p2p::Capabilities::FULL_HIST)
|
|
||||||
{
|
{
|
||||||
// if conflict, 'archive_mode' win
|
// if conflict, 'archive_mode' win
|
||||||
self.p2p_config
|
self.p2p_config
|
||||||
|
@ -185,6 +186,7 @@ impl Default for ServerConfig {
|
||||||
ServerConfig {
|
ServerConfig {
|
||||||
db_root: "grin_chain".to_string(),
|
db_root: "grin_chain".to_string(),
|
||||||
api_http_addr: "127.0.0.1:13413".to_string(),
|
api_http_addr: "127.0.0.1:13413".to_string(),
|
||||||
|
api_secret_path: Some(".api_secret".to_string()),
|
||||||
p2p_config: p2p::P2PConfig::default(),
|
p2p_config: p2p::P2PConfig::default(),
|
||||||
dandelion_config: pool::DandelionConfig::default(),
|
dandelion_config: pool::DandelionConfig::default(),
|
||||||
stratum_mining_config: Some(StratumServerConfig::default()),
|
stratum_mining_config: Some(StratumServerConfig::default()),
|
||||||
|
|
|
@ -38,6 +38,7 @@ use mining::test_miner::Miner;
|
||||||
use p2p;
|
use p2p;
|
||||||
use pool;
|
use pool;
|
||||||
use store;
|
use store;
|
||||||
|
use util::file::get_first_line;
|
||||||
use util::LOGGER;
|
use util::LOGGER;
|
||||||
|
|
||||||
/// Grin server holding internal structures.
|
/// Grin server holding internal structures.
|
||||||
|
@ -112,11 +113,10 @@ impl Server {
|
||||||
};
|
};
|
||||||
|
|
||||||
// If archive mode is enabled then the flags should contains the FULL_HIST flag
|
// If archive mode is enabled then the flags should contains the FULL_HIST flag
|
||||||
if archive_mode
|
if archive_mode && !config
|
||||||
&& !config
|
.p2p_config
|
||||||
.p2p_config
|
.capabilities
|
||||||
.capabilities
|
.contains(p2p::Capabilities::FULL_HIST)
|
||||||
.contains(p2p::Capabilities::FULL_HIST)
|
|
||||||
{
|
{
|
||||||
config
|
config
|
||||||
.p2p_config
|
.p2p_config
|
||||||
|
@ -256,12 +256,13 @@ impl Server {
|
||||||
.spawn(move || p2p_inner.listen());
|
.spawn(move || p2p_inner.listen());
|
||||||
|
|
||||||
info!(LOGGER, "Starting rest apis at: {}", &config.api_http_addr);
|
info!(LOGGER, "Starting rest apis at: {}", &config.api_http_addr);
|
||||||
|
let api_secret = get_first_line(config.api_secret_path.clone());
|
||||||
api::start_rest_apis(
|
api::start_rest_apis(
|
||||||
config.api_http_addr.clone(),
|
config.api_http_addr.clone(),
|
||||||
Arc::downgrade(&shared_chain),
|
Arc::downgrade(&shared_chain),
|
||||||
Arc::downgrade(&tx_pool),
|
Arc::downgrade(&tx_pool),
|
||||||
Arc::downgrade(&p2p_server.peers),
|
Arc::downgrade(&p2p_server.peers),
|
||||||
|
api_secret,
|
||||||
);
|
);
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
|
@ -424,8 +425,7 @@ impl Server {
|
||||||
time: time,
|
time: time,
|
||||||
duration: dur,
|
duration: dur,
|
||||||
}
|
}
|
||||||
})
|
}).collect();
|
||||||
.collect();
|
|
||||||
|
|
||||||
let block_time_sum = diff_entries.iter().fold(0, |sum, t| sum + t.duration);
|
let block_time_sum = diff_entries.iter().fold(0, |sum, t| sum + t.duration);
|
||||||
let block_diff_sum = diff_entries.iter().fold(0, |sum, d| sum + d.difficulty);
|
let block_diff_sum = diff_entries.iter().fold(0, |sum, d| sum + d.difficulty);
|
||||||
|
@ -446,8 +446,7 @@ impl Server {
|
||||||
.map(|p| {
|
.map(|p| {
|
||||||
let p = p.read().unwrap();
|
let p = p.read().unwrap();
|
||||||
PeerStats::from_peer(&p)
|
PeerStats::from_peer(&p)
|
||||||
})
|
}).collect();
|
||||||
.collect();
|
|
||||||
Ok(ServerStats {
|
Ok(ServerStats {
|
||||||
peer_count: self.peer_count(),
|
peer_count: self.peer_count(),
|
||||||
head: self.head(),
|
head: self.head(),
|
||||||
|
|
|
@ -240,13 +240,13 @@ fn test_p2p() {
|
||||||
// Tip handler function
|
// Tip handler function
|
||||||
fn get_tip(base_addr: &String, api_server_port: u16) -> Result<api::Tip, Error> {
|
fn get_tip(base_addr: &String, api_server_port: u16) -> Result<api::Tip, Error> {
|
||||||
let url = format!("http://{}:{}/v1/chain", base_addr, api_server_port);
|
let url = format!("http://{}:{}/v1/chain", base_addr, api_server_port);
|
||||||
api::client::get::<api::Tip>(url.as_str()).map_err(|e| Error::API(e))
|
api::client::get::<api::Tip>(url.as_str(), None).map_err(|e| Error::API(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status handler function
|
// Status handler function
|
||||||
fn get_status(base_addr: &String, api_server_port: u16) -> Result<api::Status, Error> {
|
fn get_status(base_addr: &String, api_server_port: u16) -> Result<api::Status, Error> {
|
||||||
let url = format!("http://{}:{}/v1/status", base_addr, api_server_port);
|
let url = format!("http://{}:{}/v1/status", base_addr, api_server_port);
|
||||||
api::client::get::<api::Status>(url.as_str()).map_err(|e| Error::API(e))
|
api::client::get::<api::Status>(url.as_str(), None).map_err(|e| Error::API(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block handler functions
|
// Block handler functions
|
||||||
|
@ -259,7 +259,7 @@ fn get_block_by_height(
|
||||||
"http://{}:{}/v1/blocks/{}",
|
"http://{}:{}/v1/blocks/{}",
|
||||||
base_addr, api_server_port, height
|
base_addr, api_server_port, height
|
||||||
);
|
);
|
||||||
api::client::get::<api::BlockPrintable>(url.as_str()).map_err(|e| Error::API(e))
|
api::client::get::<api::BlockPrintable>(url.as_str(), None).map_err(|e| Error::API(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_block_by_height_compact(
|
fn get_block_by_height_compact(
|
||||||
|
@ -271,7 +271,7 @@ fn get_block_by_height_compact(
|
||||||
"http://{}:{}/v1/blocks/{}?compact",
|
"http://{}:{}/v1/blocks/{}?compact",
|
||||||
base_addr, api_server_port, height
|
base_addr, api_server_port, height
|
||||||
);
|
);
|
||||||
api::client::get::<api::CompactBlockPrintable>(url.as_str()).map_err(|e| Error::API(e))
|
api::client::get::<api::CompactBlockPrintable>(url.as_str(), None).map_err(|e| Error::API(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_block_by_hash(
|
fn get_block_by_hash(
|
||||||
|
@ -283,7 +283,7 @@ fn get_block_by_hash(
|
||||||
"http://{}:{}/v1/blocks/{}",
|
"http://{}:{}/v1/blocks/{}",
|
||||||
base_addr, api_server_port, block_hash
|
base_addr, api_server_port, block_hash
|
||||||
);
|
);
|
||||||
api::client::get::<api::BlockPrintable>(url.as_str()).map_err(|e| Error::API(e))
|
api::client::get::<api::BlockPrintable>(url.as_str(), None).map_err(|e| Error::API(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_block_by_hash_compact(
|
fn get_block_by_hash_compact(
|
||||||
|
@ -295,7 +295,7 @@ fn get_block_by_hash_compact(
|
||||||
"http://{}:{}/v1/blocks/{}?compact",
|
"http://{}:{}/v1/blocks/{}?compact",
|
||||||
base_addr, api_server_port, block_hash
|
base_addr, api_server_port, block_hash
|
||||||
);
|
);
|
||||||
api::client::get::<api::CompactBlockPrintable>(url.as_str()).map_err(|e| Error::API(e))
|
api::client::get::<api::CompactBlockPrintable>(url.as_str(), None).map_err(|e| Error::API(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chain output handler functions
|
// Chain output handler functions
|
||||||
|
@ -310,7 +310,7 @@ fn get_outputs_by_ids1(
|
||||||
api_server_port,
|
api_server_port,
|
||||||
ids.join(",")
|
ids.join(",")
|
||||||
);
|
);
|
||||||
api::client::get::<Vec<api::Output>>(url.as_str()).map_err(|e| Error::API(e))
|
api::client::get::<Vec<api::Output>>(url.as_str(), None).map_err(|e| Error::API(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_outputs_by_ids2(
|
fn get_outputs_by_ids2(
|
||||||
|
@ -327,7 +327,7 @@ fn get_outputs_by_ids2(
|
||||||
"http://{}:{}/v1/chain/outputs/byids?{}",
|
"http://{}:{}/v1/chain/outputs/byids?{}",
|
||||||
base_addr, api_server_port, ids_string
|
base_addr, api_server_port, ids_string
|
||||||
);
|
);
|
||||||
api::client::get::<Vec<api::Output>>(url.as_str()).map_err(|e| Error::API(e))
|
api::client::get::<Vec<api::Output>>(url.as_str(), None).map_err(|e| Error::API(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_outputs_by_height(
|
fn get_outputs_by_height(
|
||||||
|
@ -340,7 +340,7 @@ fn get_outputs_by_height(
|
||||||
"http://{}:{}/v1/chain/outputs/byheight?start_height={}&end_height={}",
|
"http://{}:{}/v1/chain/outputs/byheight?start_height={}&end_height={}",
|
||||||
base_addr, api_server_port, start_height, end_height
|
base_addr, api_server_port, start_height, end_height
|
||||||
);
|
);
|
||||||
api::client::get::<Vec<api::BlockOutputs>>(url.as_str()).map_err(|e| Error::API(e))
|
api::client::get::<Vec<api::BlockOutputs>>(url.as_str(), None).map_err(|e| Error::API(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TxHashSet handler functions
|
// TxHashSet handler functions
|
||||||
|
@ -349,7 +349,7 @@ fn get_txhashset_roots(base_addr: &String, api_server_port: u16) -> Result<api::
|
||||||
"http://{}:{}/v1/txhashset/roots",
|
"http://{}:{}/v1/txhashset/roots",
|
||||||
base_addr, api_server_port
|
base_addr, api_server_port
|
||||||
);
|
);
|
||||||
api::client::get::<api::TxHashSet>(url.as_str()).map_err(|e| Error::API(e))
|
api::client::get::<api::TxHashSet>(url.as_str(), None).map_err(|e| Error::API(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_txhashset_lastoutputs(
|
fn get_txhashset_lastoutputs(
|
||||||
|
@ -369,7 +369,7 @@ fn get_txhashset_lastoutputs(
|
||||||
base_addr, api_server_port, n
|
base_addr, api_server_port, n
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
api::client::get::<Vec<api::TxHashSetNode>>(url.as_str()).map_err(|e| Error::API(e))
|
api::client::get::<Vec<api::TxHashSetNode>>(url.as_str(), None).map_err(|e| Error::API(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_txhashset_lastrangeproofs(
|
fn get_txhashset_lastrangeproofs(
|
||||||
|
@ -389,7 +389,7 @@ fn get_txhashset_lastrangeproofs(
|
||||||
base_addr, api_server_port, n
|
base_addr, api_server_port, n
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
api::client::get::<Vec<api::TxHashSetNode>>(url.as_str()).map_err(|e| Error::API(e))
|
api::client::get::<Vec<api::TxHashSetNode>>(url.as_str(), None).map_err(|e| Error::API(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_txhashset_lastkernels(
|
fn get_txhashset_lastkernels(
|
||||||
|
@ -409,7 +409,7 @@ fn get_txhashset_lastkernels(
|
||||||
base_addr, api_server_port, n
|
base_addr, api_server_port, n
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
api::client::get::<Vec<api::TxHashSetNode>>(url.as_str()).map_err(|e| Error::API(e))
|
api::client::get::<Vec<api::TxHashSetNode>>(url.as_str(), None).map_err(|e| Error::API(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to get a vec of commitment output ids from a vec of block
|
// Helper function to get a vec of commitment output ids from a vec of block
|
||||||
|
@ -430,7 +430,7 @@ pub fn ban_peer(base_addr: &String, api_server_port: u16, peer_addr: &String) ->
|
||||||
"http://{}:{}/v1/peers/{}/ban",
|
"http://{}:{}/v1/peers/{}/ban",
|
||||||
base_addr, api_server_port, peer_addr
|
base_addr, api_server_port, peer_addr
|
||||||
);
|
);
|
||||||
api::client::post_no_ret(url.as_str(), &"").map_err(|e| Error::API(e))
|
api::client::post_no_ret(url.as_str(), None, &"").map_err(|e| Error::API(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unban_peer(
|
pub fn unban_peer(
|
||||||
|
@ -442,7 +442,7 @@ pub fn unban_peer(
|
||||||
"http://{}:{}/v1/peers/{}/unban",
|
"http://{}:{}/v1/peers/{}/unban",
|
||||||
base_addr, api_server_port, peer_addr
|
base_addr, api_server_port, peer_addr
|
||||||
);
|
);
|
||||||
api::client::post_no_ret(url.as_str(), &"").map_err(|e| Error::API(e))
|
api::client::post_no_ret(url.as_str(), None, &"").map_err(|e| Error::API(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_peer(
|
pub fn get_peer(
|
||||||
|
@ -454,7 +454,7 @@ pub fn get_peer(
|
||||||
"http://{}:{}/v1/peers/{}",
|
"http://{}:{}/v1/peers/{}",
|
||||||
base_addr, api_server_port, peer_addr
|
base_addr, api_server_port, peer_addr
|
||||||
);
|
);
|
||||||
api::client::get::<p2p::PeerData>(url.as_str()).map_err(|e| Error::API(e))
|
api::client::get::<p2p::PeerData>(url.as_str(), None).map_err(|e| Error::API(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_connected_peers(
|
pub fn get_connected_peers(
|
||||||
|
@ -465,7 +465,7 @@ pub fn get_connected_peers(
|
||||||
"http://{}:{}/v1/peers/connected",
|
"http://{}:{}/v1/peers/connected",
|
||||||
base_addr, api_server_port
|
base_addr, api_server_port
|
||||||
);
|
);
|
||||||
api::client::get::<Vec<p2p::PeerInfo>>(url.as_str()).map_err(|e| Error::API(e))
|
api::client::get::<Vec<p2p::PeerInfo>>(url.as_str(), None).map_err(|e| Error::API(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all_peers(
|
pub fn get_all_peers(
|
||||||
|
@ -473,7 +473,7 @@ pub fn get_all_peers(
|
||||||
api_server_port: u16,
|
api_server_port: u16,
|
||||||
) -> Result<Vec<p2p::PeerData>, Error> {
|
) -> Result<Vec<p2p::PeerData>, Error> {
|
||||||
let url = format!("http://{}:{}/v1/peers/all", base_addr, api_server_port);
|
let url = format!("http://{}:{}/v1/peers/all", base_addr, api_server_port);
|
||||||
api::client::get::<Vec<p2p::PeerData>>(url.as_str()).map_err(|e| Error::API(e))
|
api::client::get::<Vec<p2p::PeerData>>(url.as_str(), None).map_err(|e| Error::API(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error type wrapping underlying module errors.
|
/// Error type wrapping underlying module errors.
|
||||||
|
|
|
@ -188,6 +188,7 @@ impl LocalServerContainer {
|
||||||
|
|
||||||
let s = servers::Server::new(servers::ServerConfig {
|
let s = servers::Server::new(servers::ServerConfig {
|
||||||
api_http_addr: api_addr,
|
api_http_addr: api_addr,
|
||||||
|
api_secret_path: None,
|
||||||
db_root: format!("{}/.grin", self.working_dir),
|
db_root: format!("{}/.grin", self.working_dir),
|
||||||
p2p_config: p2p::P2PConfig {
|
p2p_config: p2p::P2PConfig {
|
||||||
port: self.config.p2p_server_port,
|
port: self.config.p2p_server_port,
|
||||||
|
@ -581,6 +582,7 @@ pub fn stop_all_servers(servers: Arc<Mutex<Vec<servers::Server>>>) {
|
||||||
pub fn config(n: u16, test_name_dir: &str, seed_n: u16) -> servers::ServerConfig {
|
pub fn config(n: u16, test_name_dir: &str, seed_n: u16) -> servers::ServerConfig {
|
||||||
servers::ServerConfig {
|
servers::ServerConfig {
|
||||||
api_http_addr: format!("127.0.0.1:{}", 20000 + n),
|
api_http_addr: format!("127.0.0.1:{}", 20000 + n),
|
||||||
|
api_secret_path: None,
|
||||||
db_root: format!("target/tmp/{}/grin-sync-{}", test_name_dir, n),
|
db_root: format!("target/tmp/{}/grin-sync-{}", test_name_dir, n),
|
||||||
p2p_config: p2p::P2PConfig {
|
p2p_config: p2p::P2PConfig {
|
||||||
port: 10000 + n,
|
port: 10000 + n,
|
||||||
|
|
|
@ -127,7 +127,7 @@ fn simulate_seeding() {
|
||||||
"http://{}:{}/v1/peers/connected",
|
"http://{}:{}/v1/peers/connected",
|
||||||
&server_config.base_addr, 30020
|
&server_config.base_addr, 30020
|
||||||
);
|
);
|
||||||
let peers_all = api::client::get::<Vec<p2p::PeerInfo>>(url.as_str());
|
let peers_all = api::client::get::<Vec<p2p::PeerInfo>>(url.as_str(), None);
|
||||||
assert!(peers_all.is_ok());
|
assert!(peers_all.is_ok());
|
||||||
assert_eq!(peers_all.unwrap().len(), 4);
|
assert_eq!(peers_all.unwrap().len(), 4);
|
||||||
|
|
||||||
|
@ -279,7 +279,11 @@ fn simulate_full_sync() {
|
||||||
thread::sleep(time::Duration::from_millis(1_000));
|
thread::sleep(time::Duration::from_millis(1_000));
|
||||||
time_spent += 1;
|
time_spent += 1;
|
||||||
if time_spent >= 60 {
|
if time_spent >= 60 {
|
||||||
println!("sync fail. s2.head().height: {}, s1_header.height: {}", s2.head().height, s1_header.height);
|
println!(
|
||||||
|
"sync fail. s2.head().height: {}, s1_header.height: {}",
|
||||||
|
s2.head().height,
|
||||||
|
s1_header.height
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -475,8 +479,8 @@ fn replicate_tx_fluff_failure() {
|
||||||
let mut slate = Slate::blank(1);
|
let mut slate = Slate::blank(1);
|
||||||
|
|
||||||
wallet::controller::owner_single_use(wallet1.clone(), |api| {
|
wallet::controller::owner_single_use(wallet1.clone(), |api| {
|
||||||
slate =
|
slate = api
|
||||||
api.issue_send_tx(
|
.issue_send_tx(
|
||||||
amount, // amount
|
amount, // amount
|
||||||
2, // minimum confirmations
|
2, // minimum confirmations
|
||||||
"http://127.0.0.1:33001", // dest
|
"http://127.0.0.1:33001", // dest
|
||||||
|
|
|
@ -22,23 +22,25 @@ use config::GlobalConfig;
|
||||||
use p2p;
|
use p2p;
|
||||||
use servers::ServerConfig;
|
use servers::ServerConfig;
|
||||||
use term;
|
use term;
|
||||||
|
use util::file::get_first_line;
|
||||||
|
|
||||||
pub fn client_command(client_args: &ArgMatches, global_config: GlobalConfig) {
|
pub fn client_command(client_args: &ArgMatches, global_config: GlobalConfig) {
|
||||||
// just get defaults from the global config
|
// just get defaults from the global config
|
||||||
let server_config = global_config.members.unwrap().server;
|
let server_config = global_config.members.unwrap().server;
|
||||||
|
let api_secret = get_first_line(server_config.api_secret_path.clone());
|
||||||
|
|
||||||
match client_args.subcommand() {
|
match client_args.subcommand() {
|
||||||
("status", Some(_)) => {
|
("status", Some(_)) => {
|
||||||
show_status(&server_config);
|
show_status(&server_config, api_secret);
|
||||||
}
|
}
|
||||||
("listconnectedpeers", Some(_)) => {
|
("listconnectedpeers", Some(_)) => {
|
||||||
list_connected_peers(&server_config);
|
list_connected_peers(&server_config, api_secret);
|
||||||
}
|
}
|
||||||
("ban", Some(peer_args)) => {
|
("ban", Some(peer_args)) => {
|
||||||
let peer = peer_args.value_of("peer").unwrap();
|
let peer = peer_args.value_of("peer").unwrap();
|
||||||
|
|
||||||
if let Ok(addr) = peer.parse() {
|
if let Ok(addr) = peer.parse() {
|
||||||
ban_peer(&server_config, &addr);
|
ban_peer(&server_config, &addr, api_secret);
|
||||||
} else {
|
} else {
|
||||||
panic!("Invalid peer address format");
|
panic!("Invalid peer address format");
|
||||||
}
|
}
|
||||||
|
@ -47,7 +49,7 @@ pub fn client_command(client_args: &ArgMatches, global_config: GlobalConfig) {
|
||||||
let peer = peer_args.value_of("peer").unwrap();
|
let peer = peer_args.value_of("peer").unwrap();
|
||||||
|
|
||||||
if let Ok(addr) = peer.parse() {
|
if let Ok(addr) = peer.parse() {
|
||||||
unban_peer(&server_config, &addr);
|
unban_peer(&server_config, &addr, api_secret);
|
||||||
} else {
|
} else {
|
||||||
panic!("Invalid peer address format");
|
panic!("Invalid peer address format");
|
||||||
}
|
}
|
||||||
|
@ -56,7 +58,7 @@ pub fn client_command(client_args: &ArgMatches, global_config: GlobalConfig) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_status(config: &ServerConfig) {
|
pub fn show_status(config: &ServerConfig, api_secret: Option<String>) {
|
||||||
println!();
|
println!();
|
||||||
let title = format!("Grin Server Status");
|
let title = format!("Grin Server Status");
|
||||||
let mut t = term::stdout().unwrap();
|
let mut t = term::stdout().unwrap();
|
||||||
|
@ -65,7 +67,7 @@ pub fn show_status(config: &ServerConfig) {
|
||||||
writeln!(t, "{}", title).unwrap();
|
writeln!(t, "{}", title).unwrap();
|
||||||
writeln!(t, "--------------------------").unwrap();
|
writeln!(t, "--------------------------").unwrap();
|
||||||
t.reset().unwrap();
|
t.reset().unwrap();
|
||||||
match get_status_from_node(config) {
|
match get_status_from_node(config, api_secret) {
|
||||||
Ok(status) => {
|
Ok(status) => {
|
||||||
writeln!(e, "Protocol version: {}", status.protocol_version).unwrap();
|
writeln!(e, "Protocol version: {}", status.protocol_version).unwrap();
|
||||||
writeln!(e, "User agent: {}", status.user_agent).unwrap();
|
writeln!(e, "User agent: {}", status.user_agent).unwrap();
|
||||||
|
@ -84,7 +86,7 @@ pub fn show_status(config: &ServerConfig) {
|
||||||
println!()
|
println!()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ban_peer(config: &ServerConfig, peer_addr: &SocketAddr) {
|
pub fn ban_peer(config: &ServerConfig, peer_addr: &SocketAddr, api_secret: Option<String>) {
|
||||||
let params = "";
|
let params = "";
|
||||||
let mut e = term::stdout().unwrap();
|
let mut e = term::stdout().unwrap();
|
||||||
let url = format!(
|
let url = format!(
|
||||||
|
@ -92,14 +94,14 @@ pub fn ban_peer(config: &ServerConfig, peer_addr: &SocketAddr) {
|
||||||
config.api_http_addr,
|
config.api_http_addr,
|
||||||
peer_addr.to_string()
|
peer_addr.to_string()
|
||||||
);
|
);
|
||||||
match api::client::post_no_ret(url.as_str(), ¶ms).map_err(|e| Error::API(e)) {
|
match api::client::post_no_ret(url.as_str(), api_secret, ¶ms).map_err(|e| Error::API(e)) {
|
||||||
Ok(_) => writeln!(e, "Successfully banned peer {}", peer_addr.to_string()).unwrap(),
|
Ok(_) => writeln!(e, "Successfully banned peer {}", peer_addr.to_string()).unwrap(),
|
||||||
Err(_) => writeln!(e, "Failed to ban peer {}", peer_addr).unwrap(),
|
Err(_) => writeln!(e, "Failed to ban peer {}", peer_addr).unwrap(),
|
||||||
};
|
};
|
||||||
e.reset().unwrap();
|
e.reset().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unban_peer(config: &ServerConfig, peer_addr: &SocketAddr) {
|
pub fn unban_peer(config: &ServerConfig, peer_addr: &SocketAddr, api_secret: Option<String>) {
|
||||||
let params = "";
|
let params = "";
|
||||||
let mut e = term::stdout().unwrap();
|
let mut e = term::stdout().unwrap();
|
||||||
let url = format!(
|
let url = format!(
|
||||||
|
@ -107,17 +109,23 @@ pub fn unban_peer(config: &ServerConfig, peer_addr: &SocketAddr) {
|
||||||
config.api_http_addr,
|
config.api_http_addr,
|
||||||
peer_addr.to_string()
|
peer_addr.to_string()
|
||||||
);
|
);
|
||||||
match api::client::post_no_ret(url.as_str(), ¶ms).map_err(|e| Error::API(e)) {
|
let res: Result<(), api::Error>;
|
||||||
|
res = api::client::post_no_ret(url.as_str(), api_secret, ¶ms);
|
||||||
|
|
||||||
|
match res.map_err(|e| Error::API(e)) {
|
||||||
Ok(_) => writeln!(e, "Successfully unbanned peer {}", peer_addr).unwrap(),
|
Ok(_) => writeln!(e, "Successfully unbanned peer {}", peer_addr).unwrap(),
|
||||||
Err(_) => writeln!(e, "Failed to unban peer {}", peer_addr).unwrap(),
|
Err(_) => writeln!(e, "Failed to unban peer {}", peer_addr).unwrap(),
|
||||||
};
|
};
|
||||||
e.reset().unwrap();
|
e.reset().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_connected_peers(config: &ServerConfig) {
|
pub fn list_connected_peers(config: &ServerConfig, api_secret: Option<String>) {
|
||||||
let mut e = term::stdout().unwrap();
|
let mut e = term::stdout().unwrap();
|
||||||
let url = format!("http://{}/v1/peers/connected", config.api_http_addr);
|
let url = format!("http://{}/v1/peers/connected", config.api_http_addr);
|
||||||
match api::client::get::<Vec<p2p::PeerInfo>>(url.as_str()).map_err(|e| Error::API(e)) {
|
let peers_info: Result<Vec<p2p::PeerInfo>, api::Error>;
|
||||||
|
peers_info = api::client::get::<Vec<p2p::PeerInfo>>(url.as_str(), api_secret);
|
||||||
|
|
||||||
|
match peers_info.map_err(|e| Error::API(e)) {
|
||||||
Ok(connected_peers) => {
|
Ok(connected_peers) => {
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
for connected_peer in connected_peers {
|
for connected_peer in connected_peers {
|
||||||
|
@ -137,9 +145,12 @@ pub fn list_connected_peers(config: &ServerConfig) {
|
||||||
e.reset().unwrap();
|
e.reset().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_status_from_node(config: &ServerConfig) -> Result<api::Status, Error> {
|
fn get_status_from_node(
|
||||||
|
config: &ServerConfig,
|
||||||
|
api_secret: Option<String>,
|
||||||
|
) -> Result<api::Status, Error> {
|
||||||
let url = format!("http://{}/v1/status", config.api_http_addr);
|
let url = format!("http://{}/v1/status", config.api_http_addr);
|
||||||
api::client::get::<api::Status>(url.as_str()).map_err(|e| Error::API(e))
|
api::client::get::<api::Status>(url.as_str(), api_secret).map_err(|e| Error::API(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error type wrapping underlying module errors.
|
/// Error type wrapping underlying module errors.
|
||||||
|
|
|
@ -30,6 +30,7 @@ use grin_wallet::{self, controller, display, libwallet};
|
||||||
use grin_wallet::{HTTPWalletClient, LMDBBackend, WalletConfig, WalletInst, WalletSeed};
|
use grin_wallet::{HTTPWalletClient, LMDBBackend, WalletConfig, WalletInst, WalletSeed};
|
||||||
use keychain;
|
use keychain;
|
||||||
use servers::start_webwallet_server;
|
use servers::start_webwallet_server;
|
||||||
|
use util::file::get_first_line;
|
||||||
use util::LOGGER;
|
use util::LOGGER;
|
||||||
|
|
||||||
pub fn _init_wallet_seed(wallet_config: WalletConfig) {
|
pub fn _init_wallet_seed(wallet_config: WalletConfig) {
|
||||||
|
@ -129,6 +130,7 @@ pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) {
|
||||||
// Handle listener startup commands
|
// Handle listener startup commands
|
||||||
{
|
{
|
||||||
let wallet = instantiate_wallet(wallet_config.clone(), passphrase);
|
let wallet = instantiate_wallet(wallet_config.clone(), passphrase);
|
||||||
|
let api_secret = get_first_line(wallet_config.api_secret_path.clone());
|
||||||
match wallet_args.subcommand() {
|
match wallet_args.subcommand() {
|
||||||
("listen", Some(listen_args)) => {
|
("listen", Some(listen_args)) => {
|
||||||
if let Some(port) = listen_args.value_of("port") {
|
if let Some(port) = listen_args.value_of("port") {
|
||||||
|
@ -143,22 +145,26 @@ pub fn wallet_command(wallet_args: &ArgMatches, config: GlobalWalletConfig) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
("owner_api", Some(_api_args)) => {
|
("owner_api", Some(_api_args)) => {
|
||||||
controller::owner_listener(wallet, "127.0.0.1:13420").unwrap_or_else(|e| {
|
controller::owner_listener(wallet, "127.0.0.1:13420", api_secret).unwrap_or_else(
|
||||||
panic!(
|
|e| {
|
||||||
"Error creating wallet api listener: {:?} Config: {:?}",
|
panic!(
|
||||||
e, wallet_config
|
"Error creating wallet api listener: {:?} Config: {:?}",
|
||||||
)
|
e, wallet_config
|
||||||
});
|
)
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
("web", Some(_api_args)) => {
|
("web", Some(_api_args)) => {
|
||||||
// start owner listener and run static file server
|
// start owner listener and run static file server
|
||||||
start_webwallet_server();
|
start_webwallet_server();
|
||||||
controller::owner_listener(wallet, "127.0.0.1:13420").unwrap_or_else(|e| {
|
controller::owner_listener(wallet, "127.0.0.1:13420", api_secret).unwrap_or_else(
|
||||||
panic!(
|
|e| {
|
||||||
"Error creating wallet api listener: {:?} Config: {:?}",
|
panic!(
|
||||||
e, wallet_config
|
"Error creating wallet api listener: {:?} Config: {:?}",
|
||||||
)
|
e, wallet_config
|
||||||
});
|
)
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,6 +7,7 @@ publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
backtrace = "0.3"
|
backtrace = "0.3"
|
||||||
|
base64 = "0.9"
|
||||||
byteorder = "1"
|
byteorder = "1"
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
rand = "0.5"
|
rand = "0.5"
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io;
|
use std::io::{self, BufRead};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
|
@ -70,3 +70,18 @@ fn copy_to(src: &Path, src_type: &fs::FileType, dst: &Path) -> io::Result<u64> {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieve first line from file
|
||||||
|
pub fn get_first_line(file_path: Option<String>) -> Option<String> {
|
||||||
|
match file_path {
|
||||||
|
Some(path) => match fs::File::open(path) {
|
||||||
|
Ok(file) => {
|
||||||
|
let buf_reader = io::BufReader::new(file);
|
||||||
|
let mut lines_iter = buf_reader.lines().map(|l| l.unwrap());;
|
||||||
|
lines_iter.next()
|
||||||
|
}
|
||||||
|
Err(_) => None,
|
||||||
|
},
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
extern crate backtrace;
|
extern crate backtrace;
|
||||||
|
extern crate base64;
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -117,3 +118,8 @@ pub fn kernel_sig_msg(fee: u64, lock_height: u64) -> [u8; 32] {
|
||||||
BigEndian::write_u64(&mut bytes[24..], lock_height);
|
BigEndian::write_u64(&mut bytes[24..], lock_height);
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Encode an utf8 string to a base64 string
|
||||||
|
pub fn to_base64(s: &str) -> String {
|
||||||
|
base64::encode(s)
|
||||||
|
}
|
||||||
|
|
|
@ -44,7 +44,8 @@ pub fn compress(src_dir: &Path, dst_file: &File) -> ZipResult<()> {
|
||||||
|
|
||||||
for dent in it.filter_map(|e| e.ok()) {
|
for dent in it.filter_map(|e| e.ok()) {
|
||||||
let path = dent.path();
|
let path = dent.path();
|
||||||
let name = path.strip_prefix(Path::new(src_dir))
|
let name = path
|
||||||
|
.strip_prefix(Path::new(src_dir))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -13,27 +13,25 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
extern crate grin_util as util;
|
extern crate grin_util as util;
|
||||||
extern crate walkdir;
|
|
||||||
|
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use util::file;
|
use util::file;
|
||||||
use walkdir::WalkDir;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn copy_dir() {
|
fn copy_dir() {
|
||||||
let root = Path::new("./target/tmp2");
|
let root = Path::new("./target/tmp2");
|
||||||
fs::create_dir_all(root.join("./original/sub")).unwrap();
|
fs::create_dir_all(root.join("./original/sub")).unwrap();
|
||||||
fs::create_dir_all(root.join("./original/sub2")).unwrap();
|
fs::create_dir_all(root.join("./original/sub2")).unwrap();
|
||||||
write_files("original".to_string(),&root).unwrap();
|
write_files("original".to_string(), &root).unwrap();
|
||||||
let original_path = Path::new("./target/tmp2/original");
|
let original_path = Path::new("./target/tmp2/original");
|
||||||
let copy_path = Path::new("./target/tmp2/copy");
|
let copy_path = Path::new("./target/tmp2/copy");
|
||||||
file::copy_dir_to(original_path, copy_path).unwrap();
|
file::copy_dir_to(original_path, copy_path).unwrap();
|
||||||
let original_files = file::list_files("./target/tmp2/original".to_string());
|
let original_files = file::list_files("./target/tmp2/original".to_string());
|
||||||
let copied_files = file::list_files("./target/tmp2/copy".to_string());
|
let copied_files = file::list_files("./target/tmp2/copy".to_string());
|
||||||
for i in 1..5 {
|
for i in 1..5 {
|
||||||
assert_eq!(copied_files[i],original_files[i]);
|
assert_eq!(copied_files[i], original_files[i]);
|
||||||
}
|
}
|
||||||
fs::remove_dir_all(root).unwrap();
|
fs::remove_dir_all(root).unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,13 +13,11 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
extern crate grin_util as util;
|
extern crate grin_util as util;
|
||||||
extern crate walkdir;
|
|
||||||
|
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use util::zip;
|
use util::zip;
|
||||||
use walkdir::WalkDir;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn zip_unzip() {
|
fn zip_unzip() {
|
||||||
|
@ -27,7 +25,7 @@ fn zip_unzip() {
|
||||||
let zip_name = "./target/tmp/zipped.zip";
|
let zip_name = "./target/tmp/zipped.zip";
|
||||||
|
|
||||||
fs::create_dir_all(root.join("./to_zip/sub")).unwrap();
|
fs::create_dir_all(root.join("./to_zip/sub")).unwrap();
|
||||||
write_files("to_zip".to_string(),&root).unwrap();
|
write_files("to_zip".to_string(), &root).unwrap();
|
||||||
|
|
||||||
let zip_file = File::create(zip_name).unwrap();
|
let zip_file = File::create(zip_name).unwrap();
|
||||||
zip::compress(&root.join("./to_zip"), &zip_file).unwrap();
|
zip::compress(&root.join("./to_zip"), &zip_file).unwrap();
|
||||||
|
|
|
@ -86,7 +86,7 @@ impl WalletClient for HTTPWalletClient {
|
||||||
let url = format!("{}/v1/wallet/foreign/receive_tx", dest);
|
let url = format!("{}/v1/wallet/foreign/receive_tx", dest);
|
||||||
debug!(LOGGER, "Posting transaction slate to {}", url);
|
debug!(LOGGER, "Posting transaction slate to {}", url);
|
||||||
|
|
||||||
let res = api::client::post(url.as_str(), slate).context(
|
let res = api::client::post(url.as_str(), None, slate).context(
|
||||||
libwallet::ErrorKind::ClientCallback("Posting transaction slate"),
|
libwallet::ErrorKind::ClientCallback("Posting transaction slate"),
|
||||||
)?;
|
)?;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
|
@ -101,9 +101,9 @@ impl WalletClient for HTTPWalletClient {
|
||||||
} else {
|
} else {
|
||||||
url = format!("{}/v1/pool/push", dest);
|
url = format!("{}/v1/pool/push", dest);
|
||||||
}
|
}
|
||||||
api::client::post_no_ret(url.as_str(), tx).context(libwallet::ErrorKind::ClientCallback(
|
api::client::post_no_ret(url.as_str(), None, tx).context(
|
||||||
"Posting transaction to node",
|
libwallet::ErrorKind::ClientCallback("Posting transaction to node"),
|
||||||
))?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ impl WalletClient for HTTPWalletClient {
|
||||||
fn get_chain_height(&self) -> Result<u64, libwallet::Error> {
|
fn get_chain_height(&self) -> Result<u64, libwallet::Error> {
|
||||||
let addr = self.node_url();
|
let addr = self.node_url();
|
||||||
let url = format!("{}/v1/chain", addr);
|
let url = format!("{}/v1/chain", addr);
|
||||||
let res = api::client::get::<api::Tip>(url.as_str()).context(
|
let res = api::client::get::<api::Tip>(url.as_str(), None).context(
|
||||||
libwallet::ErrorKind::ClientCallback("Getting chain height from node"),
|
libwallet::ErrorKind::ClientCallback("Getting chain height from node"),
|
||||||
)?;
|
)?;
|
||||||
Ok(res.height)
|
Ok(res.height)
|
||||||
|
@ -136,7 +136,10 @@ impl WalletClient for HTTPWalletClient {
|
||||||
|
|
||||||
for query_chunk in query_params.chunks(500) {
|
for query_chunk in query_params.chunks(500) {
|
||||||
let url = format!("{}/v1/chain/outputs/byids?{}", addr, query_chunk.join("&"),);
|
let url = format!("{}/v1/chain/outputs/byids?{}", addr, query_chunk.join("&"),);
|
||||||
tasks.push(api::client::get_async::<Vec<api::Output>>(url.as_str()));
|
tasks.push(api::client::get_async::<Vec<api::Output>>(
|
||||||
|
url.as_str(),
|
||||||
|
None,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let task = stream::futures_unordered(tasks).collect();
|
let task = stream::futures_unordered(tasks).collect();
|
||||||
|
@ -181,7 +184,7 @@ impl WalletClient for HTTPWalletClient {
|
||||||
let mut api_outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64)> =
|
let mut api_outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64)> =
|
||||||
Vec::new();
|
Vec::new();
|
||||||
|
|
||||||
match api::client::get::<api::OutputListing>(url.as_str()) {
|
match api::client::get::<api::OutputListing>(url.as_str(), None) {
|
||||||
Ok(o) => {
|
Ok(o) => {
|
||||||
for out in o.outputs {
|
for out in o.outputs {
|
||||||
let is_coinbase = match out.output_type {
|
let is_coinbase = match out.output_type {
|
||||||
|
@ -231,7 +234,7 @@ pub fn create_coinbase(dest: &str, block_fees: &BlockFees) -> Result<CbData, Err
|
||||||
|
|
||||||
/// Makes a single request to the wallet API to create a new coinbase output.
|
/// Makes a single request to the wallet API to create a new coinbase output.
|
||||||
fn single_create_coinbase(url: &str, block_fees: &BlockFees) -> Result<CbData, Error> {
|
fn single_create_coinbase(url: &str, block_fees: &BlockFees) -> Result<CbData, Error> {
|
||||||
let res = api::client::post(url, block_fees).context(ErrorKind::GenericError(
|
let res = api::client::post(url, None, block_fees).context(ErrorKind::GenericError(
|
||||||
"Posting create coinbase".to_string(),
|
"Posting create coinbase".to_string(),
|
||||||
))?;
|
))?;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
//! Controller for wallet.. instantiates and handles listeners (or single-run
|
//! Controller for wallet.. instantiates and handles listeners (or single-run
|
||||||
//! invocations) as needed.
|
//! invocations) as needed.
|
||||||
//! Still experimental
|
//! Still experimental
|
||||||
use api::{ApiServer, Handler, ResponseFuture, Router};
|
use api::{ApiServer, BasicAuthMiddleware, Handler, ResponseFuture, Router};
|
||||||
use core::core::Transaction;
|
use core::core::Transaction;
|
||||||
use failure::ResultExt;
|
use failure::ResultExt;
|
||||||
use futures::future::{err, ok};
|
use futures::future::{err, ok};
|
||||||
|
@ -36,7 +36,7 @@ use std::net::SocketAddr;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use url::form_urlencoded;
|
use url::form_urlencoded;
|
||||||
use util::secp::pedersen;
|
use util::secp::pedersen;
|
||||||
use util::LOGGER;
|
use util::{to_base64, LOGGER};
|
||||||
|
|
||||||
/// Instantiate wallet Owner API for a single-use (command line) call
|
/// Instantiate wallet Owner API for a single-use (command line) call
|
||||||
/// Return a function containing a loaded API context to call
|
/// Return a function containing a loaded API context to call
|
||||||
|
@ -66,7 +66,11 @@ where
|
||||||
|
|
||||||
/// Listener version, providing same API but listening for requests on a
|
/// Listener version, providing same API but listening for requests on a
|
||||||
/// port and wrapping the calls
|
/// port and wrapping the calls
|
||||||
pub fn owner_listener<T: ?Sized, C, K>(wallet: Box<T>, addr: &str) -> Result<(), Error>
|
pub fn owner_listener<T: ?Sized, C, K>(
|
||||||
|
wallet: Box<T>,
|
||||||
|
addr: &str,
|
||||||
|
api_secret: Option<String>,
|
||||||
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||||
OwnerAPIHandler<T, C, K>: Handler,
|
OwnerAPIHandler<T, C, K>: Handler,
|
||||||
|
@ -77,6 +81,13 @@ where
|
||||||
let api_handler = OwnerAPIHandler::new(wallet_arc);
|
let api_handler = OwnerAPIHandler::new(wallet_arc);
|
||||||
|
|
||||||
let mut router = Router::new();
|
let mut router = Router::new();
|
||||||
|
if api_secret.is_some() {
|
||||||
|
let api_basic_auth =
|
||||||
|
"Basic ".to_string() + &to_base64(&("grin:".to_string() + &api_secret.unwrap()));
|
||||||
|
let basic_realm = "Basic realm=GrinOwnerAPI".to_string();
|
||||||
|
let basic_auth_middleware = Arc::new(BasicAuthMiddleware::new(api_basic_auth, basic_realm));
|
||||||
|
router.add_middleware(basic_auth_middleware);
|
||||||
|
}
|
||||||
router
|
router
|
||||||
.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()))?;
|
||||||
|
@ -91,7 +102,7 @@ where
|
||||||
))?;
|
))?;
|
||||||
api_thread
|
api_thread
|
||||||
.join()
|
.join()
|
||||||
.map_err(|e| ErrorKind::GenericError(format!("API thread paniced :{:?}", e)).into())
|
.map_err(|e| ErrorKind::GenericError(format!("API thread panicked :{:?}", e)).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Listener version, providing same API but listening for requests on a
|
/// Listener version, providing same API but listening for requests on a
|
||||||
|
|
|
@ -39,6 +39,8 @@ pub struct WalletConfig {
|
||||||
pub api_listen_interface: String,
|
pub api_listen_interface: String,
|
||||||
// The port this wallet will run on
|
// The port this wallet will run on
|
||||||
pub api_listen_port: u16,
|
pub api_listen_port: u16,
|
||||||
|
/// Location of the secret for basic auth on the Owner API
|
||||||
|
pub api_secret_path: Option<String>,
|
||||||
// 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,
|
||||||
|
@ -52,6 +54,7 @@ impl Default for WalletConfig {
|
||||||
chain_type: Some(ChainTypes::Testnet3),
|
chain_type: Some(ChainTypes::Testnet3),
|
||||||
api_listen_interface: "127.0.0.1".to_string(),
|
api_listen_interface: "127.0.0.1".to_string(),
|
||||||
api_listen_port: 13415,
|
api_listen_port: 13415,
|
||||||
|
api_secret_path: Some(".api_secret".to_string()),
|
||||||
check_node_api_http_addr: "http://127.0.0.1:13413".to_string(),
|
check_node_api_http_addr: "http://127.0.0.1:13413".to_string(),
|
||||||
data_file_dir: ".".to_string(),
|
data_file_dir: ".".to_string(),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue