Skip to content

Commit

Permalink
V2 API Enabling (#33)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
yeastplume authored Apr 1, 2019
1 parent af1c85a commit 0420aac
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 9 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion api/src/foreign_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
/**
Expand Down
5 changes: 4 additions & 1 deletion api/src/owner_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
/**
Expand Down
1 change: 1 addition & 0 deletions controller/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }

Expand Down
183 changes: 176 additions & 7 deletions controller/src/controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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>
Expand Down Expand Up @@ -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() {
Expand All @@ -93,17 +97,27 @@ 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);
let foreign_api_handler = ForeignAPIHandler::new(wallet.clone());
router
.add_route("/v1/wallet/foreign/**", Arc::new(foreign_api_handler))
.map_err(|_| ErrorKind::GenericError("Router failed to add route".to_string()))?;

let 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();
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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>
Expand Down

0 comments on commit 0420aac

Please sign in to comment.