2014-12-06 14:37:48 -08:00
# include "chainparamsbase.h"
# include "callrpc.h"
# include "util.h"
# include "utilstrencodings.h"
# include "rpc/protocol.h"
# include <event2/event.h>
# include <event2/http.h>
# include <event2/buffer.h>
# include <event2/keyvalq_struct.h>
using namespace std ;
/** Reply structure for request_done to fill in */
struct HTTPReply
{
int status ;
std : : string body ;
} ;
static void http_request_done ( struct evhttp_request * req , void * ctx )
{
HTTPReply * reply = static_cast < HTTPReply * > ( ctx ) ;
if ( req = = NULL ) {
/* If req is NULL, it means an error occurred while connecting, but
* I ' m not sure how to find out which one . We also don ' t really care .
*/
reply - > status = 0 ;
return ;
}
reply - > status = evhttp_request_get_response_code ( req ) ;
struct evbuffer * buf = evhttp_request_get_input_buffer ( req ) ;
if ( buf )
{
size_t size = evbuffer_get_length ( buf ) ;
const char * data = ( const char * ) evbuffer_pullup ( buf , size ) ;
if ( data )
reply - > body = std : : string ( data , size ) ;
evbuffer_drain ( buf , size ) ;
}
}
2016-12-07 09:54:00 -05:00
UniValue CallRPC ( const string & strMethod , const UniValue & params , bool connectToMainchain )
2014-12-06 14:37:48 -08:00
{
2016-06-03 09:12:39 -04:00
std : : string strhost = " -rpcconnect " ;
std : : string strport = " -rpcport " ;
std : : string struser = " -rpcuser " ;
std : : string strpassword = " -rpcpassword " ;
2016-12-07 09:54:00 -05:00
int port = GetArg ( strport , BaseParams ( ) . RPCPort ( ) ) ;
2016-06-03 09:12:39 -04:00
if ( connectToMainchain ) {
strhost = " -mainchainhost " ;
strport = " -mainchainrpcport " ;
strpassword = " -mainchainrpcpassword " ;
struser = " -mainchainrpcuser " ;
2016-12-07 09:54:00 -05:00
port = GetArg ( strport , BaseParams ( ) . MainchainRPCPort ( ) ) ;
2016-06-03 09:12:39 -04:00
}
std : : string host = GetArg ( strhost , DEFAULT_RPCHOST ) ;
2014-12-06 14:37:48 -08:00
// Create event base
struct event_base * base = event_base_new ( ) ; // TODO RAII
if ( ! base )
throw runtime_error ( " cannot create event_base " ) ;
// Synchronously look up hostname
struct evhttp_connection * evcon = evhttp_connection_base_new ( base , NULL , host . c_str ( ) , port ) ; // TODO RAII
if ( evcon = = NULL )
throw runtime_error ( " create connection failed " ) ;
evhttp_connection_set_timeout ( evcon , GetArg ( " -rpcclienttimeout " , DEFAULT_HTTP_CLIENT_TIMEOUT ) ) ;
HTTPReply response ;
struct evhttp_request * req = evhttp_request_new ( http_request_done , ( void * ) & response ) ; // TODO RAII
if ( req = = NULL )
throw runtime_error ( " create http request failed " ) ;
// Get credentials
std : : string strRPCUserColonPass ;
2016-06-03 09:12:39 -04:00
if ( mapArgs [ strpassword ] = = " " ) {
// Try fall back to cookie-based authentication if no password is provided
if ( ! connectToMainchain & & ! GetAuthCookie ( & strRPCUserColonPass ) ) {
throw runtime_error ( strprintf (
_ ( " Could not locate RPC credentials. No authentication cookie could be found, and no rpcpassword is set in the configuration file (%s) " ) ,
GetConfigFile ( ) . string ( ) . c_str ( ) ) ) ;
}
2014-12-06 14:37:48 -08:00
// Try fall back to cookie-based authentication if no password is provided
2016-06-03 09:12:39 -04:00
if ( connectToMainchain & & ! GetMainchainAuthCookie ( & strRPCUserColonPass ) ) {
2014-12-06 14:37:48 -08:00
throw runtime_error ( strprintf (
2016-12-07 09:54:00 -05:00
_ ( " Could not locate mainchain RPC credentials. No authentication cookie could be found, and no mainchainrpcpassword is set in the configuration file (%s) " ) ,
2014-12-06 14:37:48 -08:00
GetConfigFile ( ) . string ( ) . c_str ( ) ) ) ;
}
} else {
2016-12-07 09:54:00 -05:00
if ( struser = = " " )
throw runtime_error (
_ ( " Could not locate mainchain RPC credentials. No authentication cookie could be found, and no mainchainrpcuser is set in the configuration file " ) ) ;
else
strRPCUserColonPass = mapArgs [ struser ] + " : " + mapArgs [ strpassword ] ;
2014-12-06 14:37:48 -08:00
}
struct evkeyvalq * output_headers = evhttp_request_get_output_headers ( req ) ;
assert ( output_headers ) ;
evhttp_add_header ( output_headers , " Host " , host . c_str ( ) ) ;
evhttp_add_header ( output_headers , " Connection " , " close " ) ;
evhttp_add_header ( output_headers , " Authorization " , ( std : : string ( " Basic " ) + EncodeBase64 ( strRPCUserColonPass ) ) . c_str ( ) ) ;
// Attach request data
std : : string strRequest = JSONRPCRequest ( strMethod , params , 1 ) ;
struct evbuffer * output_buffer = evhttp_request_get_output_buffer ( req ) ;
assert ( output_buffer ) ;
evbuffer_add ( output_buffer , strRequest . data ( ) , strRequest . size ( ) ) ;
int r = evhttp_make_request ( evcon , req , EVHTTP_REQ_POST , " / " ) ;
if ( r ! = 0 ) {
evhttp_connection_free ( evcon ) ;
event_base_free ( base ) ;
throw CConnectionFailed ( " send http request failed " ) ;
}
event_base_dispatch ( base ) ;
evhttp_connection_free ( evcon ) ;
event_base_free ( base ) ;
if ( response . status = = 0 )
2016-08-16 17:31:27 -04:00
throw CConnectionFailed ( " couldn't connect to server (make sure daemon is running and you are using the right rpc port) " ) ;
2014-12-06 14:37:48 -08:00
else if ( response . status = = HTTP_UNAUTHORIZED )
2016-12-07 09:54:00 -05:00
if ( connectToMainchain )
throw runtime_error ( " incorrect mainchainrpcuser or mainchainrpcpassword (authorization failed) " ) ;
else
throw runtime_error ( " incorrect rpcuser or rpcpassword (authorization failed) " ) ;
2014-12-06 14:37:48 -08:00
else if ( response . status > = 400 & & response . status ! = HTTP_BAD_REQUEST & & response . status ! = HTTP_NOT_FOUND & & response . status ! = HTTP_INTERNAL_SERVER_ERROR )
throw runtime_error ( strprintf ( " server returned HTTP error %d " , response . status ) ) ;
else if ( response . body . empty ( ) )
throw runtime_error ( " no response from server " ) ;
// Parse reply
UniValue valReply ( UniValue : : VSTR ) ;
if ( ! valReply . read ( response . body ) )
throw runtime_error ( " couldn't parse reply from server " ) ;
const UniValue & reply = valReply . get_obj ( ) ;
if ( reply . empty ( ) )
throw runtime_error ( " expected reply to have result, error and id properties " ) ;
return reply ;
}
2014-12-06 14:38:47 -08:00
bool IsConfirmedBitcoinBlock ( const uint256 & genesishash , const uint256 & hash , int nMinConfirmationDepth )
{
2016-06-03 09:12:39 -04:00
2014-12-06 14:38:47 -08:00
try {
UniValue params ( UniValue : : VARR ) ;
params . push_back ( UniValue ( 0 ) ) ;
2016-12-07 09:54:00 -05:00
UniValue reply = CallRPC ( " getblockhash " , params , true ) ;
2014-12-06 14:38:47 -08:00
if ( ! find_value ( reply , " error " ) . isNull ( ) )
return false ;
UniValue result = find_value ( reply , " result " ) ;
if ( ! result . isStr ( ) )
return false ;
if ( result . get_str ( ) ! = genesishash . GetHex ( ) )
return false ;
params = UniValue ( UniValue : : VARR ) ;
params . push_back ( hash . GetHex ( ) ) ;
2016-12-07 09:54:00 -05:00
reply = CallRPC ( " getblock " , params , true ) ;
2014-12-06 14:38:47 -08:00
if ( ! find_value ( reply , " error " ) . isNull ( ) )
return false ;
result = find_value ( reply , " result " ) ;
if ( ! result . isObject ( ) )
return false ;
result = find_value ( result . get_obj ( ) , " confirmations " ) ;
return result . isNum ( ) & & result . get_int64 ( ) > = nMinConfirmationDepth ;
} catch ( CConnectionFailed & e ) {
LogPrintf ( " ERROR: Lost connection to bitcoind RPC, you will want to restart after fixing this! \n " ) ;
return false ;
} catch ( . . . ) {
LogPrintf ( " ERROR: Failure connecting to bitcoind RPC, you will want to restart after fixing this! \n " ) ;
return false ;
}
return true ;
}