// Copyright 2019 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. //! JSON-RPC Stub generation for the Foreign API use crate::keychain::Keychain; use crate::libwallet::slate::Slate; use crate::libwallet::types::{BlockFees, CbData, NodeClient, WalletBackend}; use crate::libwallet::ErrorKind; use crate::Foreign; use easy_jsonrpc; /// Public definition used to generate jsonrpc api for Foreign. #[easy_jsonrpc::rpc] pub trait ForeignRpc { /** Networked version of [Foreign::build_coinbase](struct.Foreign.html#method.build_coinbase). # Json rpc example ``` # grin_wallet_api::doctest_helper_json_rpc_foreign_assert_response!( { "jsonrpc": "2.0", "method": "build_coinbase", "params": [ { "fees": 0, "height": 0, "key_id": null } ], "id": 1 }, { "jsonrpc": "2.0", "result": { "Err": { "CallbackImpl": "Error opening wallet" } }, "id": 1 } # ); ``` */ fn build_coinbase(&self, block_fees: &BlockFees) -> Result; /** Networked version of [Foreign::verify_slate_messages](struct.Foreign.html#method.verify_slate_messages). # Json rpc example ``` # grin_wallet_api::doctest_helper_json_rpc_foreign_assert_response!( { "jsonrpc": "2.0", "method": "verify_slate_messages", "params": [ { "version_info": { "version": 2, "orig_version": 2, "min_compat_version": 0 }, "amount": 0, "fee": 0, "height": 0, "id": "414bad48-3386-4fa7-8483-72384c886ba3", "lock_height": 0, "num_participants": 2, "participant_data": [], "tx": { "body": { "inputs": [], "kernels": [], "outputs": [] }, "offset": "0000000000000000000000000000000000000000000000000000000000000000" } } ], "id": 1 }, { "jsonrpc": "2.0", "result": { "Ok": null }, "id": 1 } # ); ``` */ fn verify_slate_messages(&self, slate: &Slate) -> Result<(), ErrorKind>; /** Networked version of [Foreign::receive_tx](struct.Foreign.html#method.receive_tx). # Json rpc example ```ignore //TODO: No idea why this isn't expanding properly, check as we adjust the API # grin_wallet_api::doctest_helper_json_rpc_foreign_assert_response!( { "jsonrpc": "2.0", "method": "receive_tx", "params": [ { "version_info": { "version": 2, "orig_version": 2, "min_compat_version": 0 }, "amount": 0, "fee": 0, "height": 0, "id": "414bad48-3386-4fa7-8483-72384c886ba3", "lock_height": 0, "num_participants": 2, "participant_data": [], "tx": { "body": { "inputs": [], "kernels": [], "outputs": [] }, "offset": "0000000000000000000000000000000000000000000000000000000000000000" } }, null, null ], "id": 1 }, { "jsonrpc": "2.0", "result": { "Err": { "CallbackImpl": "Error opening wallet" } }, "id": 1 } # ); ``` */ fn receive_tx( &self, slate: Slate, dest_acct_name: Option, message: Option, ) -> Result; } impl ForeignRpc for Foreign where W: WalletBackend, C: NodeClient, K: Keychain, { fn build_coinbase(&self, block_fees: &BlockFees) -> Result { Foreign::build_coinbase(self, block_fees).map_err(|e| e.kind()) } fn verify_slate_messages(&self, slate: &Slate) -> Result<(), ErrorKind> { Foreign::verify_slate_messages(self, slate).map_err(|e| e.kind()) } fn receive_tx( &self, mut slate: Slate, dest_acct_name: Option, message: Option, ) -> Result { Foreign::receive_tx( self, &mut slate, dest_acct_name.as_ref().map(String::as_str), message, ) .map_err(|e| e.kind())?; Ok(slate) } } #[doc(hidden)] #[macro_export] macro_rules! doctest_helper_json_rpc_foreign_assert_response { ($request:tt, $expected_response:tt) => { // create temporary wallet, run jsonrpc request on api of wallet, delete wallet, return // json response. // In order to prevent leaking tempdirs, This function should not panic. fn rpc_owner_result( request: serde_json::Value, ) -> Result, String> { use easy_jsonrpc::Handler; use grin_keychain::ExtKeychain; use grin_util::Mutex; use grin_wallet_api::{Foreign, ForeignRpc}; use grin_wallet_config::WalletConfig; use grin_wallet_impls::{HTTPNodeClient, LMDBBackend}; use grin_wallet_libwallet::types::WalletBackend; use serde_json; use std::sync::Arc; use tempfile::tempdir; let dir = tempdir().map_err(|e| format!("{:#?}", e))?; { let mut wallet_config = WalletConfig::default(); wallet_config.data_file_dir = dir .path() .to_str() .ok_or("Failed to convert tmpdir path to string.".to_owned())? .to_owned(); let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None); let wallet: Arc>> = Arc::new(Mutex::new( LMDBBackend::new(wallet_config.clone(), "", node_client) .map_err(|e| format!("{:#?}", e))?, )); let api_foreign = *Foreign::new(wallet); let foreign_api = &api_foreign as &dyn ForeignRpc; Ok(foreign_api.handle_request(request)) } } let response = rpc_owner_result(serde_json::json!($request)) .unwrap() .unwrap(); let expected_response = serde_json::json!($expected_response); if response != expected_response { panic!( "(left != right) \nleft: {}\nright: {}", serde_json::to_string_pretty(&response).unwrap(), serde_json::to_string_pretty(&expected_response).unwrap() ); } }; }