From 342dbd90adac0a5c34b3778280b0934224b041df Mon Sep 17 00:00:00 2001 From: Michael Vandeberg Date: Tue, 22 Nov 2016 08:36:52 -0500 Subject: [PATCH] Allow network broadcast API to forward tx when running a read-only node. #587 --- libraries/app/api.cpp | 66 ++++++++++++++----- libraries/app/application.cpp | 18 +++++ .../app/include/steemit/app/application.hpp | 11 ++++ 3 files changed, 79 insertions(+), 16 deletions(-) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 0ceb2ff426..54de08c7cf 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -193,34 +193,68 @@ namespace steemit { namespace app { void network_broadcast_api::broadcast_transaction(const signed_transaction& trx) { trx.validate(); - FC_ASSERT( !check_max_block_age( _max_block_age ) ); - _app.chain_database()->push_transaction(trx); - _app.p2p_node()->broadcast_transaction(trx); + + if( _app._read_only ) + { + FC_ASSERT( _app._remote_net_api, "Write node RPC not configured properly or non connected." ); + (*_app._remote_net_api)->broadcast_transaction( trx ); + } + else + { + FC_ASSERT( !check_max_block_age( _max_block_age ) ); + _app.chain_database()->push_transaction(trx); + _app.p2p_node()->broadcast_transaction(trx); + } } + fc::variant network_broadcast_api::broadcast_transaction_synchronous(const signed_transaction& trx) { - promise::ptr prom( new fc::promise() ); - broadcast_transaction_with_callback( [=]( const fc::variant& v ){ - prom->set_value(v); - }, trx ); - return future(prom).wait(); + if( _app._read_only ) + { + FC_ASSERT( _app._remote_net_api, "Write node RPC not configured properly or non connected." ); + return (*_app._remote_net_api)->broadcast_transaction_synchronous( trx ); + } + else + { + promise::ptr prom( new fc::promise() ); + broadcast_transaction_with_callback( [=]( const fc::variant& v ){ + prom->set_value(v); + }, trx ); + return future(prom).wait(); + } } void network_broadcast_api::broadcast_block( const signed_block& b ) { - _app.chain_database()->push_block(b); - _app.p2p_node()->broadcast( graphene::net::block_message( b )); + if( _app._read_only ) + { + FC_ASSERT( _app._remote_net_api, "Write node RPC not configured properly or non connected." ); + (*_app._remote_net_api)->broadcast_block( b ); + } + else + { + _app.chain_database()->push_block(b); + _app.p2p_node()->broadcast( graphene::net::block_message( b )); + } } void network_broadcast_api::broadcast_transaction_with_callback(confirmation_callback cb, const signed_transaction& trx) { - FC_ASSERT( !check_max_block_age( _max_block_age ) ); - trx.validate(); - _callbacks[trx.id()] = cb; - _callbacks_expirations[trx.expiration].push_back(trx.id()); + if( _app._read_only ) + { + FC_ASSERT( _app._remote_net_api, "Write node RPC not configured properly or non connected." ); + (*_app._remote_net_api)->broadcast_transaction_with_callback( cb, trx ); + } + else + { + FC_ASSERT( !check_max_block_age( _max_block_age ) ); + trx.validate(); + _callbacks[trx.id()] = cb; + _callbacks_expirations[trx.expiration].push_back(trx.id()); - _app.chain_database()->push_transaction(trx); - _app.p2p_node()->broadcast_transaction(trx); + _app.chain_database()->push_transaction(trx); + _app.p2p_node()->broadcast_transaction(trx); + } } network_node_api::network_node_api( const api_context& a ) : _app( a.app ) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 1400914b3d..18b1f5c963 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -246,6 +246,7 @@ namespace detail { if( !read_only ) { + _self->_read_only = false; ilog( "Starting Steem node in write mode." ); _max_block_age =_options->at("max-block-age").as(); @@ -305,6 +306,22 @@ namespace detail { { ilog( "Starting Steem node in read mode." ); _chain_db->open( _data_dir / "blockchain", 0, _shared_file_size, chainbase::database::read_only ); + + if( _options->count( "read-forward-rpc" ) ) + { + try + { + auto ws_ptr = _self->_client.connect( _options->at( "read-forward-rpc" ).as< string >() ); + auto apic = std::make_shared< fc::rpc::websocket_api_connection >( *ws_ptr ); + auto login = apic->get_remote_api< login_api >( 1 ); + FC_ASSERT( login->login( "", "" ) ); + _self->_remote_net_api = login->get_api_by_name( "network_broadcast_api" )->as< network_broadcast_api >(); + } + catch( fc::exception& e ) + { + wlog( "Error conencting to remote RPC, network api forwarding disabled.", ("e", e.to_detail_string()) ); + } + } } if( _options->count("api-user") ) @@ -886,6 +903,7 @@ void application::set_program_options(boost::program_options::options_descriptio ("shared-file-size", bpo::value()->default_value("32G"), "Size of the shared memory file. Default: 32G") ("rpc-endpoint", bpo::value()->implicit_value("127.0.0.1:8090"), "Endpoint for websocket RPC to listen on") ("rpc-tls-endpoint", bpo::value()->implicit_value("127.0.0.1:8089"), "Endpoint for TLS websocket RPC to listen on") + ("read-forward-rpc", bpo::value(), "Endpoint to forward write API calls to for a read node" ) ("server-pem,p", bpo::value()->implicit_value("server.pem"), "The TLS certificate file for this server") ("server-pem-password,P", bpo::value()->implicit_value(""), "Password for this certificate") ("api-user", bpo::value< vector >()->composing(), "API user specification, may be specified multiple times") diff --git a/libraries/app/include/steemit/app/application.hpp b/libraries/app/include/steemit/app/application.hpp index c0c002c2fd..2228fa6dd6 100644 --- a/libraries/app/include/steemit/app/application.hpp +++ b/libraries/app/include/steemit/app/application.hpp @@ -31,6 +31,7 @@ #include #include +#include #include @@ -42,6 +43,9 @@ namespace steemit { namespace app { class plugin; class application; + class network_broadcast_api; + class login_api; + class application { public: @@ -117,6 +121,13 @@ namespace steemit { namespace app { void get_max_block_age( int32_t& result ); + bool _read_only = true; + fc::optional< fc::api< network_broadcast_api > > _remote_net_api; + fc::optional< fc::api< login_api > > _remote_login; + fc::http::websocket_connection_ptr _ws_ptr; + std::shared_ptr< fc::rpc::websocket_api_connection > _ws_apic; + fc::http::websocket_client _client; + private: std::shared_ptr my;