mirror of
https://github.com/mimblewimble/grin-wallet.git
synced 2025-01-21 03:21:08 +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"
|
version = "1.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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 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)",
|
"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)",
|
"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 crate::Foreign;
|
||||||
use easy_jsonrpc;
|
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]
|
#[easy_jsonrpc::rpc]
|
||||||
pub trait ForeignRpc {
|
pub trait ForeignRpc {
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -26,7 +26,10 @@ use crate::libwallet::ErrorKind;
|
||||||
use crate::Owner;
|
use crate::Owner;
|
||||||
use easy_jsonrpc;
|
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]
|
#[easy_jsonrpc::rpc]
|
||||||
pub trait OwnerRpc {
|
pub trait OwnerRpc {
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -30,6 +30,7 @@ tokio-retry = "0.1"
|
||||||
uuid = { version = "0.7", features = ["serde", "v4"] }
|
uuid = { version = "0.7", features = ["serde", "v4"] }
|
||||||
url = "1.7.0"
|
url = "1.7.0"
|
||||||
chrono = { version = "0.4.4", features = ["serde"] }
|
chrono = { version = "0.4.4", features = ["serde"] }
|
||||||
|
easy-jsonrpc = "0.4.1"
|
||||||
|
|
||||||
grin_wallet_util = { path = "../util", version = "1.1.0" }
|
grin_wallet_util = { path = "../util", version = "1.1.0" }
|
||||||
|
|
||||||
|
|
|
@ -15,8 +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 crate::api::{ApiServer, BasicAuthMiddleware, Handler, ResponseFuture, Router, TLSConfig};
|
use crate::api::{self, ApiServer, BasicAuthMiddleware, ResponseFuture, Router, TLSConfig};
|
||||||
use crate::apiwallet::{Foreign, Owner};
|
|
||||||
use crate::core::core;
|
use crate::core::core;
|
||||||
use crate::core::core::Transaction;
|
use crate::core::core::Transaction;
|
||||||
use crate::impls::{FileWalletCommAdapter, HTTPWalletCommAdapter, KeybaseWalletCommAdapter};
|
use crate::impls::{FileWalletCommAdapter, HTTPWalletCommAdapter, KeybaseWalletCommAdapter};
|
||||||
|
@ -42,6 +41,10 @@ use std::sync::Arc;
|
||||||
use url::form_urlencoded;
|
use url::form_urlencoded;
|
||||||
use uuid::Uuid;
|
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
|
/// 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
|
||||||
pub fn owner_single_use<F, T: ?Sized, C, K>(wallet: Arc<Mutex<T>>, f: F) -> Result<(), Error>
|
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>
|
) -> 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>: api::Handler,
|
||||||
C: NodeClient + 'static,
|
C: NodeClient + 'static,
|
||||||
K: Keychain + 'static,
|
K: Keychain + 'static,
|
||||||
{
|
{
|
||||||
let api_handler = OwnerAPIHandler::new(wallet.clone());
|
let api_handler = OwnerAPIHandler::new(wallet.clone());
|
||||||
|
let api_handler_v2 = OwnerAPIHandlerV2::new(wallet.clone());
|
||||||
|
|
||||||
let mut router = Router::new();
|
let mut router = Router::new();
|
||||||
if api_secret.is_some() {
|
if api_secret.is_some() {
|
||||||
|
@ -93,10 +97,15 @@ where
|
||||||
let basic_auth_middleware = Arc::new(BasicAuthMiddleware::new(api_basic_auth, basic_realm));
|
let basic_auth_middleware = Arc::new(BasicAuthMiddleware::new(api_basic_auth, basic_realm));
|
||||||
router.add_middleware(basic_auth_middleware);
|
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()))?;
|
||||||
|
|
||||||
|
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 so configured, add the foreign API to the same port
|
||||||
if owner_api_include_foreign.unwrap_or(false) {
|
if owner_api_include_foreign.unwrap_or(false) {
|
||||||
info!("Starting HTTP Foreign API on Owner server at {}.", addr);
|
info!("Starting HTTP Foreign API on Owner server at {}.", addr);
|
||||||
|
@ -104,6 +113,11 @@ where
|
||||||
router
|
router
|
||||||
.add_route("/v1/wallet/foreign/**", Arc::new(foreign_api_handler))
|
.add_route("/v1/wallet/foreign/**", Arc::new(foreign_api_handler))
|
||||||
.map_err(|_| ErrorKind::GenericError("Router failed to add route".to_string()))?;
|
.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();
|
let mut apis = ApiServer::new();
|
||||||
|
@ -131,13 +145,18 @@ where
|
||||||
C: NodeClient + 'static,
|
C: NodeClient + 'static,
|
||||||
K: Keychain + '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();
|
let mut router = Router::new();
|
||||||
router
|
router
|
||||||
.add_route("/v1/wallet/foreign/**", Arc::new(api_handler))
|
.add_route("/v1/wallet/foreign/**", 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()))?;
|
||||||
|
|
||||||
|
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();
|
let mut apis = ApiServer::new();
|
||||||
warn!("Starting HTTP Foreign listener API server at {}.", addr);
|
warn!("Starting HTTP Foreign listener 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");
|
||||||
|
@ -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
|
where
|
||||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||||
C: NodeClient + '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>
|
pub struct ForeignAPIHandler<T: ?Sized, C, K>
|
||||||
where
|
where
|
||||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
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
|
where
|
||||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||||
C: NodeClient + 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
|
// Utility to serialize a struct into JSON and produce a sensible Response
|
||||||
// out of it.
|
// out of it.
|
||||||
fn json_response<T>(s: &T) -> Response<Body>
|
fn json_response<T>(s: &T) -> Response<Body>
|
||||||
|
|
Loading…
Reference in a new issue