This reverts commit 8db1b930f3.
Since cli_wallet itself makes no effort to be stateless, we
can simply unlock in a separate call. Removing the ability
to use the header to unlock reduces the potential for user
confusion about whether the HTTP API provides security
(it does not).
246 lines
10 KiB
C++
246 lines
10 KiB
C++
/*
|
|
* Copyright (c) 2015, Cryptonomex, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
|
|
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted until September 8, 2015, provided that the following conditions are met:
|
|
*
|
|
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <algorithm>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
|
|
#include <fc/io/json.hpp>
|
|
#include <fc/io/stdio.hpp>
|
|
#include <fc/network/http/server.hpp>
|
|
#include <fc/network/http/websocket.hpp>
|
|
#include <fc/rpc/cli.hpp>
|
|
#include <fc/rpc/http_api.hpp>
|
|
#include <fc/rpc/websocket_api.hpp>
|
|
|
|
#include <graphene/app/api.hpp>
|
|
#include <graphene/chain/address.hpp>
|
|
#include <graphene/utilities/key_conversion.hpp>
|
|
#include <graphene/wallet/wallet.hpp>
|
|
|
|
#include <fc/rpc/websocket_api.hpp>
|
|
|
|
#include <fc/interprocess/signals.hpp>
|
|
#include <boost/program_options.hpp>
|
|
|
|
#include <fc/log/console_appender.hpp>
|
|
#include <fc/log/file_appender.hpp>
|
|
#include <fc/log/logger.hpp>
|
|
#include <fc/log/logger_config.hpp>
|
|
#ifndef WIN32
|
|
#include <csignal>
|
|
#endif
|
|
|
|
using namespace graphene::app;
|
|
using namespace graphene::chain;
|
|
using namespace graphene::utilities;
|
|
using namespace graphene::wallet;
|
|
using namespace std;
|
|
namespace bpo = boost::program_options;
|
|
|
|
int main( int argc, char** argv )
|
|
{
|
|
try {
|
|
|
|
boost::program_options::options_description opts;
|
|
opts.add_options()
|
|
("help,h", "Print this help message and exit.")
|
|
("server-rpc-endpoint,s", bpo::value<string>()->implicit_value("ws://127.0.0.1:8090"), "Server websocket RPC endpoint")
|
|
("server-rpc-user,u", bpo::value<string>(), "Server Username")
|
|
("server-rpc-password,p", bpo::value<string>(), "Server Password")
|
|
("rpc-endpoint,r", bpo::value<string>()->implicit_value("127.0.0.1:8091"), "Endpoint for wallet websocket RPC to listen on")
|
|
("rpc-tls-endpoint,t", bpo::value<string>()->implicit_value("127.0.0.1:8092"), "Endpoint for wallet websocket TLS RPC to listen on")
|
|
("rpc-tls-certificate,c", bpo::value<string>()->implicit_value("server.pem"), "PEM certificate for wallet websocket TLS RPC")
|
|
("rpc-http-endpoint,h", bpo::value<string>()->implicit_value("127.0.0.1:8093"), "Endpoint for wallet HTTP RPC to listen on")
|
|
("daemon,d", "Run the wallet in daemon mode" )
|
|
("wallet-file,w", bpo::value<string>()->implicit_value("wallet.json"), "wallet to load");
|
|
|
|
bpo::variables_map options;
|
|
|
|
bpo::store( bpo::parse_command_line(argc, argv, opts), options );
|
|
|
|
if( options.count("help") )
|
|
{
|
|
std::cout << opts << "\n";
|
|
return 0;
|
|
}
|
|
|
|
fc::path data_dir;
|
|
fc::logging_config cfg;
|
|
fc::path log_dir = data_dir / "logs";
|
|
|
|
fc::file_appender::config ac;
|
|
ac.filename = log_dir / "rpc" / "rpc.log";
|
|
ac.flush = true;
|
|
ac.rotate = true;
|
|
ac.rotation_interval = fc::hours( 1 );
|
|
ac.rotation_limit = fc::days( 1 );
|
|
|
|
std::cout << "Logging RPC to file: " << (data_dir / ac.filename).preferred_string() << "\n";
|
|
|
|
cfg.appenders.push_back(fc::appender_config( "default", "console", fc::variant(fc::console_appender::config())));
|
|
cfg.appenders.push_back(fc::appender_config( "rpc", "file", fc::variant(ac)));
|
|
|
|
cfg.loggers = { fc::logger_config("default"), fc::logger_config( "rpc") };
|
|
cfg.loggers.front().level = fc::log_level::info;
|
|
cfg.loggers.front().appenders = {"default"};
|
|
cfg.loggers.back().level = fc::log_level::debug;
|
|
cfg.loggers.back().appenders = {"rpc"};
|
|
|
|
//fc::configure_logging( cfg );
|
|
|
|
fc::ecc::private_key genesis_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")));
|
|
|
|
idump( (key_to_wif( genesis_private_key ) ) );
|
|
|
|
fc::ecc::private_key nathan_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan")));
|
|
idump( (key_to_wif( nathan_private_key ) ) );
|
|
|
|
//
|
|
// TODO: We read wallet_data twice, once in main() to grab the
|
|
// socket info, again in wallet_api when we do
|
|
// load_wallet_file(). Seems like this could be better
|
|
// designed.
|
|
//
|
|
wallet_data wdata;
|
|
|
|
fc::path wallet_file( options.count("wallet-file") ? options.at("wallet-file").as<string>() : "wallet.json");
|
|
if( fc::exists( wallet_file ) )
|
|
wdata = fc::json::from_file( wallet_file ).as<wallet_data>();
|
|
|
|
if( options.count("server-rpc-endpoint") )
|
|
wdata.ws_server = options.at("server-rpc-endpoint").as<std::string>();
|
|
if( options.count("server-rpc-user") )
|
|
wdata.ws_user = options.at("server-rpc-user").as<std::string>();
|
|
if( options.count("server-rpc-password") )
|
|
wdata.ws_password = options.at("server-rpc-password").as<std::string>();
|
|
|
|
fc::http::websocket_client client;
|
|
auto con = client.connect( wdata.ws_server );
|
|
auto apic = std::make_shared<fc::rpc::websocket_api_connection>(*con);
|
|
|
|
auto remote_api = apic->get_remote_api< login_api >(1);
|
|
FC_ASSERT( remote_api->login( wdata.ws_user, wdata.ws_password ) );
|
|
|
|
auto wapiptr = std::make_shared<wallet_api>(remote_api);
|
|
wapiptr->set_wallet_filename( wallet_file.generic_string() );
|
|
wapiptr->load_wallet_file();
|
|
|
|
fc::api<wallet_api> wapi(wapiptr);
|
|
|
|
auto wallet_cli = std::make_shared<fc::rpc::cli>();
|
|
for( auto& name_formatter : wapiptr->get_result_formatters() )
|
|
wallet_cli->format_result( name_formatter.first, name_formatter.second );
|
|
|
|
boost::signals2::scoped_connection closed_connection(con->closed.connect([]{
|
|
cerr << "Server has disconnected us.\n";
|
|
}));
|
|
(void)(closed_connection);
|
|
|
|
if( wapiptr->is_new() )
|
|
{
|
|
std::cout << "Please use the set_password method to initialize a new wallet before continuing\n";
|
|
wallet_cli->set_prompt( "new >>> " );
|
|
} else
|
|
wallet_cli->set_prompt( "locked >>> " );
|
|
|
|
boost::signals2::scoped_connection locked_connection(wapiptr->lock_changed.connect([&](bool locked) {
|
|
wallet_cli->set_prompt( locked ? "locked >>> " : "unlocked >>> " );
|
|
}));
|
|
|
|
auto _websocket_server = std::make_shared<fc::http::websocket_server>();
|
|
if( options.count("rpc-endpoint") )
|
|
{
|
|
_websocket_server->on_connection([&]( const fc::http::websocket_connection_ptr& c ){
|
|
std::cout << "here... \n";
|
|
wlog("." );
|
|
auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(*c);
|
|
wsc->register_api(wapi);
|
|
c->set_session_data( wsc );
|
|
});
|
|
ilog( "Listening for incoming RPC requests on ${p}", ("p", options.at("rpc-endpoint").as<string>() ));
|
|
_websocket_server->listen( fc::ip::endpoint::from_string(options.at("rpc-endpoint").as<string>()) );
|
|
_websocket_server->start_accept();
|
|
}
|
|
|
|
string cert_pem = "server.pem";
|
|
if( options.count( "rpc-tls-certificate" ) )
|
|
cert_pem = options.at("rpc-tls-certificate").as<string>();
|
|
|
|
auto _websocket_tls_server = std::make_shared<fc::http::websocket_tls_server>(cert_pem);
|
|
if( options.count("rpc-tls-endpoint") )
|
|
{
|
|
_websocket_tls_server->on_connection([&]( const fc::http::websocket_connection_ptr& c ){
|
|
auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(*c);
|
|
wsc->register_api(wapi);
|
|
c->set_session_data( wsc );
|
|
});
|
|
ilog( "Listening for incoming TLS RPC requests on ${p}", ("p", options.at("rpc-tls-endpoint").as<string>() ));
|
|
_websocket_tls_server->listen( fc::ip::endpoint::from_string(options.at("rpc-tls-endpoint").as<string>()) );
|
|
_websocket_tls_server->start_accept();
|
|
}
|
|
|
|
auto _http_server = std::make_shared<fc::http::server>();
|
|
if( options.count("rpc-http-endpoint" ) )
|
|
{
|
|
ilog( "Listening for incoming HTTP RPC requests on ${p}", ("p", options.at("rpc-http-endpoint").as<string>() ) );
|
|
_http_server->listen( fc::ip::endpoint::from_string( options.at( "rpc-http-endpoint" ).as<string>() ) );
|
|
//
|
|
// due to implementation, on_request() must come AFTER listen()
|
|
//
|
|
_http_server->on_request(
|
|
[&]( const fc::http::request& req, const fc::http::server::response& resp )
|
|
{
|
|
std::shared_ptr< fc::rpc::http_api_connection > conn =
|
|
std::make_shared< fc::rpc::http_api_connection>();
|
|
conn->register_api( wapi );
|
|
conn->on_request( req, resp );
|
|
} );
|
|
}
|
|
|
|
if( !options.count( "daemon" ) )
|
|
{
|
|
wallet_cli->register_api( wapi );
|
|
wallet_cli->start();
|
|
wallet_cli->wait();
|
|
}
|
|
else
|
|
{
|
|
fc::promise<int>::ptr exit_promise = new fc::promise<int>("UNIX Signal Handler");
|
|
#ifdef __unix__
|
|
fc::set_signal_handler([&exit_promise](int signal) {
|
|
exit_promise->set_value(signal);
|
|
}, SIGINT);
|
|
#endif
|
|
|
|
ilog( "Entering Daemon Mode, ^C to exit" );
|
|
exit_promise->wait();
|
|
}
|
|
|
|
wapi->save_wallet_file(wallet_file.generic_string());
|
|
locked_connection.disconnect();
|
|
closed_connection.disconnect();
|
|
}
|
|
catch ( const fc::exception& e )
|
|
{
|
|
std::cout << e.to_detail_string() << "\n";
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|