mirror of
https://github.com/mimblewimble/grin-wallet.git
synced 2025-01-20 19:11:09 +03:00
V2 API Enabling (#33)
* node height return value and documentation * rustfmt * add parameter struct for initiate tx functions * rustfmt * change tx estimate to use InitTxArgs * rustfmt * transaction estimate * rustfmt * initiate tx args fixed * add send args to init * rustfmt * last owner api documentation * api 2 wiring * rustfmt * wiring in V2 JSON-RPC API * rustfmt * some documentation on endpoint * shorten endpoints
This commit is contained in:
parent
427f42c552
commit
4bbce85b75
5 changed files with 186 additions and 9 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -806,6 +806,7 @@ name = "grin_wallet_controller"
|
|||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"easy-jsonrpc 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -21,7 +21,10 @@ use crate::libwallet::ErrorKind;
|
|||
use crate::Foreign;
|
||||
use easy_jsonrpc;
|
||||
|
||||
/// Public definition used to generate jsonrpc api for Foreign.
|
||||
/// Public definition used to generate Foreign jsonrpc api.
|
||||
/// * When running `grin-wallet listen` with defaults, the V2 api is available at
|
||||
/// `localhost:3415/v2/foreign`
|
||||
/// * The endpoint only supports POST operations, with the json-rpc request as the body
|
||||
#[easy_jsonrpc::rpc]
|
||||
pub trait ForeignRpc {
|
||||
/**
|
||||
|
|
|
@ -26,7 +26,10 @@ use crate::libwallet::ErrorKind;
|
|||
use crate::Owner;
|
||||
use easy_jsonrpc;
|
||||
|
||||
/// Public definition used to generate jsonrpc api for Owner.
|
||||
/// Public definition used to generate Owner jsonrpc api.
|
||||
/// * When running `grin-wallet listen` with defaults, the V2 api is available at
|
||||
/// `localhost:3420/v2/owner`
|
||||
/// * The endpoint only supports POST operations, with the json-rpc request as the body
|
||||
#[easy_jsonrpc::rpc]
|
||||
pub trait OwnerRpc {
|
||||
/**
|
||||
|
|
|
@ -30,6 +30,7 @@ tokio-retry = "0.1"
|
|||
uuid = { version = "0.7", features = ["serde", "v4"] }
|
||||
url = "1.7.0"
|
||||
chrono = { version = "0.4.4", features = ["serde"] }
|
||||
easy-jsonrpc = "0.4.1"
|
||||
|
||||
grin_wallet_util = { path = "../util", version = "1.1.0" }
|
||||
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
//! Controller for wallet.. instantiates and handles listeners (or single-run
|
||||
//! invocations) as needed.
|
||||
//! Still experimental
|
||||
use crate::api::{ApiServer, BasicAuthMiddleware, Handler, ResponseFuture, Router, TLSConfig};
|
||||
use crate::apiwallet::{Foreign, Owner};
|
||||
use crate::api::{self, ApiServer, BasicAuthMiddleware, ResponseFuture, Router, TLSConfig};
|
||||
use crate::core::core;
|
||||
use crate::core::core::Transaction;
|
||||
use crate::impls::{FileWalletCommAdapter, HTTPWalletCommAdapter, KeybaseWalletCommAdapter};
|
||||
|
@ -42,6 +41,10 @@ use std::sync::Arc;
|
|||
use url::form_urlencoded;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::apiwallet::{Foreign, ForeignRpc, Owner, OwnerRpc};
|
||||
use easy_jsonrpc;
|
||||
use easy_jsonrpc::Handler;
|
||||
|
||||
/// Instantiate wallet Owner API for a single-use (command line) call
|
||||
/// Return a function containing a loaded API context to call
|
||||
pub fn owner_single_use<F, T: ?Sized, C, K>(wallet: Arc<Mutex<T>>, f: F) -> Result<(), Error>
|
||||
|
@ -79,11 +82,12 @@ pub fn owner_listener<T: ?Sized, C, K>(
|
|||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
OwnerAPIHandler<T, C, K>: Handler,
|
||||
OwnerAPIHandler<T, C, K>: api::Handler,
|
||||
C: NodeClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
let api_handler = OwnerAPIHandler::new(wallet.clone());
|
||||
let api_handler_v2 = OwnerAPIHandlerV2::new(wallet.clone());
|
||||
|
||||
let mut router = Router::new();
|
||||
if api_secret.is_some() {
|
||||
|
@ -93,10 +97,15 @@ where
|
|||
let basic_auth_middleware = Arc::new(BasicAuthMiddleware::new(api_basic_auth, basic_realm));
|
||||
router.add_middleware(basic_auth_middleware);
|
||||
}
|
||||
|
||||
router
|
||||
.add_route("/v1/wallet/owner/**", Arc::new(api_handler))
|
||||
.map_err(|_| ErrorKind::GenericError("Router failed to add route".to_string()))?;
|
||||
|
||||
router
|
||||
.add_route("/v2/owner", Arc::new(api_handler_v2))
|
||||
.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);
|
||||
|
@ -104,6 +113,11 @@ where
|
|||
router
|
||||
.add_route("/v1/wallet/foreign/**", Arc::new(foreign_api_handler))
|
||||
.map_err(|_| ErrorKind::GenericError("Router failed to add route".to_string()))?;
|
||||
|
||||
let foreign_api_handler_v2 = ForeignAPIHandlerV2::new(wallet.clone());
|
||||
router
|
||||
.add_route("/v2/foreign", Arc::new(foreign_api_handler_v2))
|
||||
.map_err(|_| ErrorKind::GenericError("Router failed to add route".to_string()))?;
|
||||
}
|
||||
|
||||
let mut apis = ApiServer::new();
|
||||
|
@ -131,13 +145,18 @@ where
|
|||
C: NodeClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
let api_handler = ForeignAPIHandler::new(wallet);
|
||||
let api_handler = ForeignAPIHandler::new(wallet.clone());
|
||||
let api_handler_v2 = ForeignAPIHandlerV2::new(wallet);
|
||||
|
||||
let mut router = Router::new();
|
||||
router
|
||||
.add_route("/v1/wallet/foreign/**", Arc::new(api_handler))
|
||||
.map_err(|_| ErrorKind::GenericError("Router failed to add route".to_string()))?;
|
||||
|
||||
router
|
||||
.add_route("/v2/foreign", Arc::new(api_handler_v2))
|
||||
.map_err(|_| ErrorKind::GenericError("Router failed to add route".to_string()))?;
|
||||
|
||||
let mut apis = ApiServer::new();
|
||||
warn!("Starting HTTP Foreign listener API server at {}.", addr);
|
||||
let socket_addr: SocketAddr = addr.parse().expect("unable to parse socket address");
|
||||
|
@ -592,7 +611,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized, C, K> Handler for OwnerAPIHandler<T, C, K>
|
||||
impl<T: ?Sized, C, K> api::Handler for OwnerAPIHandler<T, C, K>
|
||||
where
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
C: NodeClient + 'static,
|
||||
|
@ -624,8 +643,83 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// API Handler/Wrapper for foreign functions
|
||||
/// V2 API Handler/Wrapper for owner functions
|
||||
pub struct OwnerAPIHandlerV2<T: ?Sized, C, K>
|
||||
where
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
C: NodeClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
/// Wallet instance
|
||||
pub wallet: Arc<Mutex<T>>,
|
||||
phantom: PhantomData<K>,
|
||||
phantom_c: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized, C, K> OwnerAPIHandlerV2<T, C, K>
|
||||
where
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
C: NodeClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
/// Create a new owner API handler for GET methods
|
||||
pub fn new(wallet: Arc<Mutex<T>>) -> OwnerAPIHandlerV2<T, C, K> {
|
||||
OwnerAPIHandlerV2 {
|
||||
wallet,
|
||||
phantom: PhantomData,
|
||||
phantom_c: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn call_api(
|
||||
&self,
|
||||
req: Request<Body>,
|
||||
api: Owner<T, C, K>,
|
||||
) -> Box<dyn Future<Item = serde_json::Value, Error = Error> + Send> {
|
||||
Box::new(parse_body(req).and_then(move |val: serde_json::Value| {
|
||||
let owner_api = &api as &dyn OwnerRpc;
|
||||
match owner_api.handle_request(val) {
|
||||
Some(r) => ok(r),
|
||||
None => {
|
||||
error!("OwnerAPI: call_api: failed with error");
|
||||
err(ErrorKind::GenericError(format!("OwnerAPI Call Failed")).into())
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
fn handle_post_request(&self, req: Request<Body>) -> WalletResponseFuture {
|
||||
let api = Owner::new(self.wallet.clone());
|
||||
Box::new(
|
||||
self.call_api(req, api)
|
||||
.and_then(|resp| ok(json_response_pretty(&resp))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized, C, K> api::Handler for OwnerAPIHandlerV2<T, C, K>
|
||||
where
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
C: NodeClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
fn post(&self, req: Request<Body>) -> ResponseFuture {
|
||||
Box::new(
|
||||
self.handle_post_request(req)
|
||||
.and_then(|r| ok(r))
|
||||
.or_else(|e| {
|
||||
error!("Request Error: {:?}", e);
|
||||
ok(create_error_response(e))
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn options(&self, _req: Request<Body>) -> ResponseFuture {
|
||||
Box::new(ok(create_ok_response("{}")))
|
||||
}
|
||||
}
|
||||
|
||||
/// API Handler/Wrapper for foreign functions
|
||||
pub struct ForeignAPIHandler<T: ?Sized, C, K>
|
||||
where
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
|
@ -710,7 +804,7 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
impl<T: ?Sized, C, K> Handler for ForeignAPIHandler<T, C, K>
|
||||
impl<T: ?Sized, C, K> api::Handler for ForeignAPIHandler<T, C, K>
|
||||
where
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
C: NodeClient + Send + Sync + 'static,
|
||||
|
@ -728,6 +822,81 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// V2 API Handler/Wrapper for foreign functions
|
||||
pub struct ForeignAPIHandlerV2<T: ?Sized, C, K>
|
||||
where
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
C: NodeClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
/// Wallet instance
|
||||
pub wallet: Arc<Mutex<T>>,
|
||||
phantom: PhantomData<K>,
|
||||
phantom_c: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized, C, K> ForeignAPIHandlerV2<T, C, K>
|
||||
where
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
C: NodeClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
/// Create a new foreign API handler for GET methods
|
||||
pub fn new(wallet: Arc<Mutex<T>>) -> ForeignAPIHandlerV2<T, C, K> {
|
||||
ForeignAPIHandlerV2 {
|
||||
wallet,
|
||||
phantom: PhantomData,
|
||||
phantom_c: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn call_api(
|
||||
&self,
|
||||
req: Request<Body>,
|
||||
api: Foreign<T, C, K>,
|
||||
) -> Box<dyn Future<Item = serde_json::Value, Error = Error> + Send> {
|
||||
Box::new(parse_body(req).and_then(move |val: serde_json::Value| {
|
||||
let foreign_api = &api as &dyn ForeignRpc;
|
||||
match foreign_api.handle_request(val) {
|
||||
Some(r) => ok(r),
|
||||
None => {
|
||||
error!("ForeignAPI: call_api: failed with error");
|
||||
err(ErrorKind::GenericError(format!("ForeignAPI Call Failed")).into())
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
fn handle_post_request(&self, req: Request<Body>) -> WalletResponseFuture {
|
||||
let api = Foreign::new(self.wallet.clone());
|
||||
Box::new(
|
||||
self.call_api(req, api)
|
||||
.and_then(|resp| ok(json_response_pretty(&resp))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized, C, K> api::Handler for ForeignAPIHandlerV2<T, C, K>
|
||||
where
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
C: NodeClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
fn post(&self, req: Request<Body>) -> ResponseFuture {
|
||||
Box::new(
|
||||
self.handle_post_request(req)
|
||||
.and_then(|r| ok(r))
|
||||
.or_else(|e| {
|
||||
error!("Request Error: {:?}", e);
|
||||
ok(create_error_response(e))
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn options(&self, _req: Request<Body>) -> ResponseFuture {
|
||||
Box::new(ok(create_ok_response("{}")))
|
||||
}
|
||||
}
|
||||
// Utility to serialize a struct into JSON and produce a sensible Response
|
||||
// out of it.
|
||||
fn json_response<T>(s: &T) -> Response<Body>
|
||||
|
|
Loading…
Reference in a new issue