2015-05-27 12:08:46 +00:00
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
# include "RpcServer.h"
# include <future>
# include <unordered_map>
// CryptoNote
2015-07-30 15:22:07 +00:00
# include "Common/StringTools.h"
# include "CryptoNoteCore/CryptoNoteTools.h"
# include "CryptoNoteCore/Core.h"
# include "CryptoNoteCore/IBlock.h"
# include "CryptoNoteCore/Miner.h"
# include "CryptoNoteCore/TransactionExtra.h"
# include "P2p/NetNode.h"
# include "CoreRpcServerErrorCodes.h"
2015-05-27 12:08:46 +00:00
# include "JsonRpc.h"
# undef ERROR
using namespace Logging ;
2015-07-30 15:22:07 +00:00
using namespace Crypto ;
using namespace Common ;
2015-05-27 12:08:46 +00:00
namespace CryptoNote {
namespace {
template < typename Command >
RpcServer : : HandlerFunction binMethod ( bool ( RpcServer : : * handler ) ( typename Command : : request const & , typename Command : : response & ) ) {
return [ handler ] ( RpcServer * obj , const HttpRequest & request , HttpResponse & response ) {
boost : : value_initialized < typename Command : : request > req ;
boost : : value_initialized < typename Command : : response > res ;
2015-07-15 12:23:00 +00:00
if ( ! loadFromBinaryKeyValue ( static_cast < typename Command : : request & > ( req ) , request . getBody ( ) ) ) {
2015-05-27 12:08:46 +00:00
return false ;
}
bool result = ( obj - > * handler ) ( req , res ) ;
2015-07-15 12:23:00 +00:00
response . setBody ( storeToBinaryKeyValue ( res . data ( ) ) ) ;
2015-05-27 12:08:46 +00:00
return result ;
} ;
}
template < typename Command >
RpcServer : : HandlerFunction jsonMethod ( bool ( RpcServer : : * handler ) ( typename Command : : request const & , typename Command : : response & ) ) {
return [ handler ] ( RpcServer * obj , const HttpRequest & request , HttpResponse & response ) {
boost : : value_initialized < typename Command : : request > req ;
boost : : value_initialized < typename Command : : response > res ;
2015-07-15 12:23:00 +00:00
if ( ! loadFromJson ( static_cast < typename Command : : request & > ( req ) , request . getBody ( ) ) ) {
2015-05-27 12:08:46 +00:00
return false ;
}
bool result = ( obj - > * handler ) ( req , res ) ;
2015-07-15 12:23:00 +00:00
response . setBody ( storeToJson ( res . data ( ) ) ) ;
2015-05-27 12:08:46 +00:00
return result ;
} ;
}
}
std : : unordered_map < std : : string , RpcServer : : HandlerFunction > RpcServer : : s_handlers = {
// binary handlers
{ " /getblocks.bin " , binMethod < COMMAND_RPC_GET_BLOCKS_FAST > ( & RpcServer : : on_get_blocks ) } ,
{ " /queryblocks.bin " , binMethod < COMMAND_RPC_QUERY_BLOCKS > ( & RpcServer : : on_query_blocks ) } ,
2015-07-30 15:22:07 +00:00
{ " /queryblockslite.bin " , binMethod < COMMAND_RPC_QUERY_BLOCKS_LITE > ( & RpcServer : : on_query_blocks_lite ) } ,
2015-05-27 12:08:46 +00:00
{ " /get_o_indexes.bin " , binMethod < COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES > ( & RpcServer : : on_get_indexes ) } ,
{ " /getrandom_outs.bin " , binMethod < COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS > ( & RpcServer : : on_get_random_outs ) } ,
2015-07-15 12:23:00 +00:00
{ " /get_pool_changes.bin " , binMethod < COMMAND_RPC_GET_POOL_CHANGES > ( & RpcServer : : onGetPoolChanges ) } ,
2015-07-30 15:22:07 +00:00
{ " /get_pool_changes_lite.bin " , binMethod < COMMAND_RPC_GET_POOL_CHANGES_LITE > ( & RpcServer : : onGetPoolChangesLite ) } ,
2015-05-27 12:08:46 +00:00
// json handlers
{ " /getinfo " , jsonMethod < COMMAND_RPC_GET_INFO > ( & RpcServer : : on_get_info ) } ,
{ " /getheight " , jsonMethod < COMMAND_RPC_GET_HEIGHT > ( & RpcServer : : on_get_height ) } ,
{ " /gettransactions " , jsonMethod < COMMAND_RPC_GET_TRANSACTIONS > ( & RpcServer : : on_get_transactions ) } ,
{ " /sendrawtransaction " , jsonMethod < COMMAND_RPC_SEND_RAW_TX > ( & RpcServer : : on_send_raw_tx ) } ,
{ " /start_mining " , jsonMethod < COMMAND_RPC_START_MINING > ( & RpcServer : : on_start_mining ) } ,
{ " /stop_mining " , jsonMethod < COMMAND_RPC_STOP_MINING > ( & RpcServer : : on_stop_mining ) } ,
{ " /stop_daemon " , jsonMethod < COMMAND_RPC_STOP_DAEMON > ( & RpcServer : : on_stop_daemon ) } ,
// json rpc
{ " /json_rpc " , std : : bind ( & RpcServer : : processJsonRpcRequest , std : : placeholders : : _1 , std : : placeholders : : _2 , std : : placeholders : : _3 ) }
} ;
2015-07-30 15:22:07 +00:00
RpcServer : : RpcServer ( System : : Dispatcher & dispatcher , Logging : : ILogger & log , core & c , NodeServer & p2p ) :
2015-05-27 12:08:46 +00:00
HttpServer ( dispatcher , log ) , logger ( log , " RpcServer " ) , m_core ( c ) , m_p2p ( p2p ) {
}
void RpcServer : : processRequest ( const HttpRequest & request , HttpResponse & response ) {
auto url = request . getUrl ( ) ;
auto it = s_handlers . find ( url ) ;
if ( it = = s_handlers . end ( ) ) {
response . setStatus ( HttpResponse : : STATUS_404 ) ;
return ;
}
if ( url ! = " /json_rpc " & & ! checkCoreReady ( ) ) {
response . setStatus ( HttpResponse : : STATUS_500 ) ;
response . setBody ( " Core is busy " ) ;
return ;
}
it - > second ( this , request , response ) ;
}
bool RpcServer : : processJsonRpcRequest ( const HttpRequest & request , HttpResponse & response ) {
using namespace JsonRpc ;
response . addHeader ( " Content-Type " , " application/json " ) ;
JsonRpcRequest jsonRequest ;
JsonRpcResponse jsonResponse ;
try {
2015-07-15 12:23:00 +00:00
logger ( TRACE ) < < " JSON-RPC request: " < < request . getBody ( ) ;
2015-05-27 12:08:46 +00:00
jsonRequest . parseRequest ( request . getBody ( ) ) ;
jsonResponse . setId ( jsonRequest . getId ( ) ) ; // copy id
static std : : unordered_map < std : : string , JsonMemberMethod > jsonRpcHandlers = {
{ " getblockcount " , makeMemberMethod ( & RpcServer : : on_getblockcount ) } ,
{ " on_getblockhash " , makeMemberMethod ( & RpcServer : : on_getblockhash ) } ,
{ " getblocktemplate " , makeMemberMethod ( & RpcServer : : on_getblocktemplate ) } ,
{ " getcurrencyid " , makeMemberMethod ( & RpcServer : : on_get_currency_id ) } ,
{ " submitblock " , makeMemberMethod ( & RpcServer : : on_submitblock ) } ,
{ " getlastblockheader " , makeMemberMethod ( & RpcServer : : on_get_last_block_header ) } ,
{ " getblockheaderbyhash " , makeMemberMethod ( & RpcServer : : on_get_block_header_by_hash ) } ,
{ " getblockheaderbyheight " , makeMemberMethod ( & RpcServer : : on_get_block_header_by_height ) }
} ;
auto it = jsonRpcHandlers . find ( jsonRequest . getMethod ( ) ) ;
if ( it = = jsonRpcHandlers . end ( ) ) {
throw JsonRpcError ( JsonRpc : : errMethodNotFound ) ;
}
if ( jsonRequest . getMethod ( ) ! = " getcurrencyid " & & ! checkCoreReady ( ) ) {
throw JsonRpcError ( CORE_RPC_ERROR_CODE_CORE_BUSY , " Core is busy " ) ;
}
it - > second ( this , jsonRequest , jsonResponse ) ;
} catch ( const JsonRpcError & err ) {
jsonResponse . setError ( err ) ;
2015-07-15 12:23:00 +00:00
} catch ( const std : : exception & e ) {
jsonResponse . setError ( JsonRpcError ( JsonRpc : : errInternalError , e . what ( ) ) ) ;
2015-05-27 12:08:46 +00:00
}
response . setBody ( jsonResponse . getBody ( ) ) ;
2015-07-15 12:23:00 +00:00
logger ( TRACE ) < < " JSON-RPC response: " < < jsonResponse . getBody ( ) ;
2015-05-27 12:08:46 +00:00
return true ;
}
# define CHECK_CORE_READY()
bool RpcServer : : checkCoreReady ( ) {
2015-07-15 12:23:00 +00:00
return m_core . is_ready ( ) & & m_p2p . get_payload_object ( ) . isSynchronized ( ) ;
2015-05-27 12:08:46 +00:00
}
//
// Binary handlers
//
bool RpcServer : : on_get_blocks ( const COMMAND_RPC_GET_BLOCKS_FAST : : request & req , COMMAND_RPC_GET_BLOCKS_FAST : : response & res ) {
2015-07-30 15:22:07 +00:00
// TODO code duplication see InProcessNode::doGetNewBlocks()
if ( req . block_ids . empty ( ) ) {
res . status = " Failed " ;
return false ;
}
if ( req . block_ids . back ( ) ! = m_core . getBlockIdByHeight ( 0 ) ) {
2015-05-27 12:08:46 +00:00
res . status = " Failed " ;
return false ;
}
2015-07-30 15:22:07 +00:00
uint32_t totalBlockCount ;
uint32_t startBlockIndex ;
std : : vector < Crypto : : Hash > supplement = m_core . findBlockchainSupplement ( req . block_ids , COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT , totalBlockCount , startBlockIndex ) ;
res . current_height = totalBlockCount ;
res . start_height = startBlockIndex ;
for ( const auto & blockId : supplement ) {
assert ( m_core . have_block ( blockId ) ) ;
auto completeBlock = m_core . getBlock ( blockId ) ;
assert ( completeBlock ! = nullptr ) ;
2015-05-27 12:08:46 +00:00
res . blocks . resize ( res . blocks . size ( ) + 1 ) ;
2015-07-30 15:22:07 +00:00
res . blocks . back ( ) . block = asString ( toBinaryArray ( completeBlock - > getBlock ( ) ) ) ;
res . blocks . back ( ) . txs . reserve ( completeBlock - > getTransactionCount ( ) ) ;
for ( size_t i = 0 ; i < completeBlock - > getTransactionCount ( ) ; + + i ) {
res . blocks . back ( ) . txs . push_back ( asString ( toBinaryArray ( completeBlock - > getTransaction ( i ) ) ) ) ;
2015-05-27 12:08:46 +00:00
}
}
res . status = CORE_RPC_STATUS_OK ;
return true ;
}
bool RpcServer : : on_query_blocks ( const COMMAND_RPC_QUERY_BLOCKS : : request & req , COMMAND_RPC_QUERY_BLOCKS : : response & res ) {
CHECK_CORE_READY ( ) ;
2015-07-30 15:22:07 +00:00
uint32_t startHeight ;
uint32_t currentHeight ;
uint32_t fullOffset ;
if ( ! m_core . queryBlocks ( req . block_ids , req . timestamp , startHeight , currentHeight , fullOffset , res . items ) ) {
res . status = " Failed to perform query " ;
return false ;
}
res . start_height = startHeight ;
res . current_height = currentHeight ;
res . full_offset = fullOffset ;
res . status = CORE_RPC_STATUS_OK ;
return true ;
}
bool RpcServer : : on_query_blocks_lite ( const COMMAND_RPC_QUERY_BLOCKS_LITE : : request & req , COMMAND_RPC_QUERY_BLOCKS_LITE : : response & res ) {
CHECK_CORE_READY ( ) ;
uint32_t startHeight ;
uint32_t currentHeight ;
uint32_t fullOffset ;
if ( ! m_core . queryBlocksLite ( req . blockIds , req . timestamp , startHeight , currentHeight , fullOffset , res . items ) ) {
2015-05-27 12:08:46 +00:00
res . status = " Failed to perform query " ;
return false ;
}
2015-07-30 15:22:07 +00:00
res . startHeight = startHeight ;
res . currentHeight = currentHeight ;
res . fullOffset = fullOffset ;
2015-05-27 12:08:46 +00:00
res . status = CORE_RPC_STATUS_OK ;
return true ;
}
bool RpcServer : : on_get_indexes ( const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES : : request & req , COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES : : response & res ) {
CHECK_CORE_READY ( ) ;
2015-07-30 15:22:07 +00:00
std : : vector < uint32_t > outputIndexes ;
if ( ! m_core . get_tx_outputs_gindexs ( req . txid , outputIndexes ) ) {
2015-05-27 12:08:46 +00:00
res . status = " Failed " ;
return true ;
}
2015-07-30 15:22:07 +00:00
res . o_indexes . assign ( outputIndexes . begin ( ) , outputIndexes . end ( ) ) ;
2015-05-27 12:08:46 +00:00
res . status = CORE_RPC_STATUS_OK ;
logger ( TRACE ) < < " COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES: [ " < < res . o_indexes . size ( ) < < " ] " ;
return true ;
}
bool RpcServer : : on_get_random_outs ( const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : request & req , COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : response & res ) {
CHECK_CORE_READY ( ) ;
res . status = " Failed " ;
if ( ! m_core . get_random_outs_for_amounts ( req , res ) ) {
return true ;
}
res . status = CORE_RPC_STATUS_OK ;
std : : stringstream ss ;
typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : outs_for_amount outs_for_amount ;
typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : out_entry out_entry ;
std : : for_each ( res . outs . begin ( ) , res . outs . end ( ) , [ & ] ( outs_for_amount & ofa ) {
ss < < " [ " < < ofa . amount < < " ]: " ;
assert ( ofa . outs . size ( ) & & " internal error: ofa.outs.size() is empty " ) ;
std : : for_each ( ofa . outs . begin ( ) , ofa . outs . end ( ) , [ & ] ( out_entry & oe )
{
ss < < oe . global_amount_index < < " " ;
} ) ;
ss < < ENDL ;
} ) ;
std : : string s = ss . str ( ) ;
logger ( TRACE ) < < " COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: " < < ENDL < < s ;
res . status = CORE_RPC_STATUS_OK ;
return true ;
}
2015-07-15 12:23:00 +00:00
bool RpcServer : : onGetPoolChanges ( const COMMAND_RPC_GET_POOL_CHANGES : : request & req , COMMAND_RPC_GET_POOL_CHANGES : : response & rsp ) {
CHECK_CORE_READY ( ) ;
rsp . status = CORE_RPC_STATUS_OK ;
std : : vector < CryptoNote : : Transaction > addedTransactions ;
rsp . isTailBlockActual = m_core . getPoolChanges ( req . tailBlockId , req . knownTxsIds , addedTransactions , rsp . deletedTxsIds ) ;
2015-08-27 18:55:14 +00:00
for ( auto & tx : addedTransactions ) {
BinaryArray txBlob ;
if ( ! toBinaryArray ( tx , txBlob ) ) {
rsp . status = " Internal error " ;
break ; ;
2015-07-15 12:23:00 +00:00
}
2015-08-27 18:55:14 +00:00
rsp . addedTxs . emplace_back ( std : : move ( txBlob ) ) ;
}
2015-07-15 12:23:00 +00:00
return true ;
}
2015-07-30 15:22:07 +00:00
bool RpcServer : : onGetPoolChangesLite ( const COMMAND_RPC_GET_POOL_CHANGES_LITE : : request & req , COMMAND_RPC_GET_POOL_CHANGES_LITE : : response & rsp ) {
CHECK_CORE_READY ( ) ;
rsp . status = CORE_RPC_STATUS_OK ;
rsp . isTailBlockActual = m_core . getPoolChangesLite ( req . tailBlockId , req . knownTxsIds , rsp . addedTxs , rsp . deletedTxsIds ) ;
return true ;
}
2015-05-27 12:08:46 +00:00
//
// JSON handlers
//
bool RpcServer : : on_get_info ( const COMMAND_RPC_GET_INFO : : request & req , COMMAND_RPC_GET_INFO : : response & res ) {
res . height = m_core . get_current_blockchain_height ( ) ;
2015-07-30 15:22:07 +00:00
res . difficulty = m_core . getNextBlockDifficulty ( ) ;
res . tx_count = m_core . get_blockchain_total_transactions ( ) - res . height ; //without coinbase
2015-05-27 12:08:46 +00:00
res . tx_pool_size = m_core . get_pool_transactions_count ( ) ;
2015-07-30 15:22:07 +00:00
res . alt_blocks_count = m_core . get_alternative_blocks_count ( ) ;
2015-05-27 12:08:46 +00:00
uint64_t total_conn = m_p2p . get_connections_count ( ) ;
res . outgoing_connections_count = m_p2p . get_outgoing_connections_count ( ) ;
res . incoming_connections_count = total_conn - res . outgoing_connections_count ;
2015-07-30 15:22:07 +00:00
res . white_peerlist_size = m_p2p . getPeerlistManager ( ) . get_white_peers_count ( ) ;
res . grey_peerlist_size = m_p2p . getPeerlistManager ( ) . get_gray_peers_count ( ) ;
2015-05-27 12:08:46 +00:00
res . status = CORE_RPC_STATUS_OK ;
return true ;
}
bool RpcServer : : on_get_height ( const COMMAND_RPC_GET_HEIGHT : : request & req , COMMAND_RPC_GET_HEIGHT : : response & res ) {
CHECK_CORE_READY ( ) ;
res . height = m_core . get_current_blockchain_height ( ) ;
res . status = CORE_RPC_STATUS_OK ;
return true ;
}
bool RpcServer : : on_get_transactions ( const COMMAND_RPC_GET_TRANSACTIONS : : request & req , COMMAND_RPC_GET_TRANSACTIONS : : response & res ) {
CHECK_CORE_READY ( ) ;
2015-07-30 15:22:07 +00:00
std : : vector < Hash > vh ;
2015-05-27 12:08:46 +00:00
for ( const auto & tx_hex_str : req . txs_hashes ) {
2015-07-30 15:22:07 +00:00
BinaryArray b ;
if ( ! fromHex ( tx_hex_str , b ) )
2015-05-27 12:08:46 +00:00
{
res . status = " Failed to parse hex representation of transaction hash " ;
return true ;
}
2015-07-30 15:22:07 +00:00
if ( b . size ( ) ! = sizeof ( Hash ) )
2015-05-27 12:08:46 +00:00
{
res . status = " Failed, size of data mismatch " ;
}
2015-07-30 15:22:07 +00:00
vh . push_back ( * reinterpret_cast < const Hash * > ( b . data ( ) ) ) ;
2015-05-27 12:08:46 +00:00
}
2015-07-30 15:22:07 +00:00
std : : list < Hash > missed_txs ;
2015-05-27 12:08:46 +00:00
std : : list < Transaction > txs ;
2015-07-15 12:23:00 +00:00
m_core . getTransactions ( vh , txs , missed_txs ) ;
2015-05-27 12:08:46 +00:00
for ( auto & tx : txs ) {
2015-07-30 15:22:07 +00:00
res . txs_as_hex . push_back ( toHex ( toBinaryArray ( tx ) ) ) ;
2015-05-27 12:08:46 +00:00
}
for ( const auto & miss_tx : missed_txs ) {
res . missed_tx . push_back ( Common : : podToHex ( miss_tx ) ) ;
}
res . status = CORE_RPC_STATUS_OK ;
return true ;
}
bool RpcServer : : on_send_raw_tx ( const COMMAND_RPC_SEND_RAW_TX : : request & req , COMMAND_RPC_SEND_RAW_TX : : response & res ) {
CHECK_CORE_READY ( ) ;
2015-07-30 15:22:07 +00:00
BinaryArray tx_blob ;
if ( ! fromHex ( req . tx_as_hex , tx_blob ) )
2015-05-27 12:08:46 +00:00
{
logger ( INFO ) < < " [on_send_raw_tx]: Failed to parse tx from hexbuff: " < < req . tx_as_hex ;
res . status = " Failed " ;
return true ;
}
2015-07-15 12:23:00 +00:00
tx_verification_context tvc = boost : : value_initialized < tx_verification_context > ( ) ;
2015-05-27 12:08:46 +00:00
if ( ! m_core . handle_incoming_tx ( tx_blob , tvc , false ) )
{
logger ( INFO ) < < " [on_send_raw_tx]: Failed to process tx " ;
res . status = " Failed " ;
return true ;
}
if ( tvc . m_verifivation_failed )
{
logger ( INFO ) < < " [on_send_raw_tx]: tx verification failed " ;
res . status = " Failed " ;
return true ;
}
if ( ! tvc . m_should_be_relayed )
{
logger ( INFO ) < < " [on_send_raw_tx]: tx accepted, but not relayed " ;
res . status = " Not relayed " ;
return true ;
}
NOTIFY_NEW_TRANSACTIONS : : request r ;
2015-07-30 15:22:07 +00:00
r . txs . push_back ( asString ( tx_blob ) ) ;
2015-05-27 12:08:46 +00:00
m_core . get_protocol ( ) - > relay_transactions ( r ) ;
//TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes
res . status = CORE_RPC_STATUS_OK ;
return true ;
}
bool RpcServer : : on_start_mining ( const COMMAND_RPC_START_MINING : : request & req , COMMAND_RPC_START_MINING : : response & res ) {
CHECK_CORE_READY ( ) ;
AccountPublicAddress adr ;
if ( ! m_core . currency ( ) . parseAccountAddressString ( req . miner_address , adr ) ) {
res . status = " Failed, wrong address " ;
return true ;
}
2015-07-30 15:22:07 +00:00
if ( ! m_core . get_miner ( ) . start ( adr , static_cast < size_t > ( req . threads_count ) ) ) {
2015-05-27 12:08:46 +00:00
res . status = " Failed, mining not started " ;
return true ;
}
res . status = CORE_RPC_STATUS_OK ;
return true ;
}
bool RpcServer : : on_stop_mining ( const COMMAND_RPC_STOP_MINING : : request & req , COMMAND_RPC_STOP_MINING : : response & res ) {
CHECK_CORE_READY ( ) ;
if ( ! m_core . get_miner ( ) . stop ( ) ) {
res . status = " Failed, mining not stopped " ;
return true ;
}
res . status = CORE_RPC_STATUS_OK ;
return true ;
}
bool RpcServer : : on_stop_daemon ( const COMMAND_RPC_STOP_DAEMON : : request & req , COMMAND_RPC_STOP_DAEMON : : response & res ) {
CHECK_CORE_READY ( ) ;
if ( m_core . currency ( ) . isTestnet ( ) ) {
2015-07-30 15:22:07 +00:00
m_p2p . sendStopSignal ( ) ;
2015-05-27 12:08:46 +00:00
res . status = CORE_RPC_STATUS_OK ;
} else {
res . status = CORE_RPC_ERROR_CODE_INTERNAL_ERROR ;
return false ;
}
return true ;
}
//------------------------------------------------------------------------------------------------------------------------------
// JSON RPC methods
//------------------------------------------------------------------------------------------------------------------------------
bool RpcServer : : on_getblockcount ( const COMMAND_RPC_GETBLOCKCOUNT : : request & req , COMMAND_RPC_GETBLOCKCOUNT : : response & res ) {
res . count = m_core . get_current_blockchain_height ( ) ;
res . status = CORE_RPC_STATUS_OK ;
return true ;
}
bool RpcServer : : on_getblockhash ( const COMMAND_RPC_GETBLOCKHASH : : request & req , COMMAND_RPC_GETBLOCKHASH : : response & res ) {
if ( req . size ( ) ! = 1 ) {
throw JsonRpc : : JsonRpcError { CORE_RPC_ERROR_CODE_WRONG_PARAM , " Wrong parameters, expected height " } ;
}
2015-07-30 15:22:07 +00:00
uint32_t h = static_cast < uint32_t > ( req [ 0 ] ) ;
Crypto : : Hash blockId = m_core . getBlockIdByHeight ( h ) ;
if ( blockId = = NULL_HASH ) {
2015-05-27 12:08:46 +00:00
throw JsonRpc : : JsonRpcError {
CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT ,
std : : string ( " To big height: " ) + std : : to_string ( h ) + " , current blockchain height = " + std : : to_string ( m_core . get_current_blockchain_height ( ) )
} ;
}
2015-07-30 15:22:07 +00:00
res = Common : : podToHex ( blockId ) ;
2015-05-27 12:08:46 +00:00
return true ;
}
namespace {
uint64_t slow_memmem ( void * start_buff , size_t buflen , void * pat , size_t patlen )
{
void * buf = start_buff ;
void * end = ( char * ) buf + buflen - patlen ;
while ( ( buf = memchr ( buf , ( ( char * ) pat ) [ 0 ] , buflen ) ) )
{
if ( buf > end )
return 0 ;
if ( memcmp ( buf , pat , patlen ) = = 0 )
return ( char * ) buf - ( char * ) start_buff ;
buf = ( char * ) buf + 1 ;
}
return 0 ;
}
}
bool RpcServer : : on_getblocktemplate ( const COMMAND_RPC_GETBLOCKTEMPLATE : : request & req , COMMAND_RPC_GETBLOCKTEMPLATE : : response & res ) {
if ( req . reserve_size > TX_EXTRA_NONCE_MAX_COUNT ) {
throw JsonRpc : : JsonRpcError { CORE_RPC_ERROR_CODE_TOO_BIG_RESERVE_SIZE , " To big reserved size, maximum 255 " } ;
}
2015-07-15 12:23:00 +00:00
AccountPublicAddress acc = boost : : value_initialized < AccountPublicAddress > ( ) ;
2015-05-27 12:08:46 +00:00
if ( ! req . wallet_address . size ( ) | | ! m_core . currency ( ) . parseAccountAddressString ( req . wallet_address , acc ) ) {
throw JsonRpc : : JsonRpcError { CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS , " Failed to parse wallet address " } ;
}
2015-07-15 12:23:00 +00:00
Block b = boost : : value_initialized < Block > ( ) ;
2015-07-30 15:22:07 +00:00
CryptoNote : : BinaryArray blob_reserve ;
2015-05-27 12:08:46 +00:00
blob_reserve . resize ( req . reserve_size , 0 ) ;
if ( ! m_core . get_block_template ( b , acc , res . difficulty , res . height , blob_reserve ) ) {
logger ( ERROR ) < < " Failed to create block template " ;
throw JsonRpc : : JsonRpcError { CORE_RPC_ERROR_CODE_INTERNAL_ERROR , " Internal error: failed to create block template " } ;
}
2015-07-30 15:22:07 +00:00
BinaryArray block_blob = toBinaryArray ( b ) ;
PublicKey tx_pub_key = CryptoNote : : getTransactionPublicKeyFromExtra ( b . baseTransaction . extra ) ;
if ( tx_pub_key = = NULL_PUBLIC_KEY ) {
2015-05-27 12:08:46 +00:00
logger ( ERROR ) < < " Failed to find tx pub key in coinbase extra " ;
throw JsonRpc : : JsonRpcError { CORE_RPC_ERROR_CODE_INTERNAL_ERROR , " Internal error: failed to find tx pub key in coinbase extra " } ;
}
if ( 0 < req . reserve_size ) {
res . reserved_offset = slow_memmem ( ( void * ) block_blob . data ( ) , block_blob . size ( ) , & tx_pub_key , sizeof ( tx_pub_key ) ) ;
if ( ! res . reserved_offset ) {
logger ( ERROR ) < < " Failed to find tx pub key in blockblob " ;
throw JsonRpc : : JsonRpcError { CORE_RPC_ERROR_CODE_INTERNAL_ERROR , " Internal error: failed to create block template " } ;
}
res . reserved_offset + = sizeof ( tx_pub_key ) + 3 ; //3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte)
if ( res . reserved_offset + req . reserve_size > block_blob . size ( ) ) {
logger ( ERROR ) < < " Failed to calculate offset for reserved bytes " ;
throw JsonRpc : : JsonRpcError { CORE_RPC_ERROR_CODE_INTERNAL_ERROR , " Internal error: failed to create block template " } ;
}
} else {
res . reserved_offset = 0 ;
}
2015-07-30 15:22:07 +00:00
res . blocktemplate_blob = toHex ( block_blob ) ;
2015-05-27 12:08:46 +00:00
res . status = CORE_RPC_STATUS_OK ;
return true ;
}
bool RpcServer : : on_get_currency_id ( const COMMAND_RPC_GET_CURRENCY_ID : : request & /*req*/ , COMMAND_RPC_GET_CURRENCY_ID : : response & res ) {
2015-07-30 15:22:07 +00:00
Hash currencyId = m_core . currency ( ) . genesisBlockHash ( ) ;
res . currency_id_blob = Common : : podToHex ( currencyId ) ;
2015-05-27 12:08:46 +00:00
return true ;
}
bool RpcServer : : on_submitblock ( const COMMAND_RPC_SUBMITBLOCK : : request & req , COMMAND_RPC_SUBMITBLOCK : : response & res ) {
if ( req . size ( ) ! = 1 ) {
throw JsonRpc : : JsonRpcError { CORE_RPC_ERROR_CODE_WRONG_PARAM , " Wrong param " } ;
}
2015-07-30 15:22:07 +00:00
BinaryArray blockblob ;
if ( ! fromHex ( req [ 0 ] , blockblob ) ) {
2015-05-27 12:08:46 +00:00
throw JsonRpc : : JsonRpcError { CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB , " Wrong block blob " } ;
}
2015-07-15 12:23:00 +00:00
block_verification_context bvc = boost : : value_initialized < block_verification_context > ( ) ;
2015-05-27 12:08:46 +00:00
2015-07-30 15:22:07 +00:00
m_core . handle_incoming_block_blob ( blockblob , bvc , true , true ) ;
2015-05-27 12:08:46 +00:00
if ( ! bvc . m_added_to_main_chain ) {
throw JsonRpc : : JsonRpcError { CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED , " Block not accepted " } ;
}
res . status = CORE_RPC_STATUS_OK ;
return true ;
}
namespace {
uint64_t get_block_reward ( const Block & blk ) {
uint64_t reward = 0 ;
2015-07-30 15:22:07 +00:00
for ( const TransactionOutput & out : blk . baseTransaction . outputs ) {
2015-05-27 12:08:46 +00:00
reward + = out . amount ;
}
return reward ;
}
}
2015-07-30 15:22:07 +00:00
void RpcServer : : fill_block_header_response ( const Block & blk , bool orphan_status , uint64_t height , const Hash & hash , block_header_response & responce ) {
2015-05-27 12:08:46 +00:00
responce . major_version = blk . majorVersion ;
responce . minor_version = blk . minorVersion ;
responce . timestamp = blk . timestamp ;
2015-07-30 15:22:07 +00:00
responce . prev_hash = Common : : podToHex ( blk . previousBlockHash ) ;
2015-05-27 12:08:46 +00:00
responce . nonce = blk . nonce ;
responce . orphan_status = orphan_status ;
responce . height = height ;
responce . depth = m_core . get_current_blockchain_height ( ) - height - 1 ;
responce . hash = Common : : podToHex ( hash ) ;
2015-07-30 15:22:07 +00:00
m_core . getBlockDifficulty ( static_cast < uint32_t > ( height ) , responce . difficulty ) ;
2015-05-27 12:08:46 +00:00
responce . reward = get_block_reward ( blk ) ;
}
bool RpcServer : : on_get_last_block_header ( const COMMAND_RPC_GET_LAST_BLOCK_HEADER : : request & req , COMMAND_RPC_GET_LAST_BLOCK_HEADER : : response & res ) {
2015-07-30 15:22:07 +00:00
uint32_t last_block_height ;
Hash last_block_hash ;
2015-05-27 12:08:46 +00:00
2015-07-30 15:22:07 +00:00
m_core . get_blockchain_top ( last_block_height , last_block_hash ) ;
2015-05-27 12:08:46 +00:00
Block last_block ;
2015-07-15 12:23:00 +00:00
if ( ! m_core . getBlockByHash ( last_block_hash , last_block ) ) {
2015-05-27 12:08:46 +00:00
throw JsonRpc : : JsonRpcError { CORE_RPC_ERROR_CODE_INTERNAL_ERROR , " Internal error: can't get last block hash. " } ;
}
2015-07-30 15:22:07 +00:00
fill_block_header_response ( last_block , false , last_block_height , last_block_hash , res . block_header ) ;
2015-05-27 12:08:46 +00:00
res . status = CORE_RPC_STATUS_OK ;
return true ;
}
bool RpcServer : : on_get_block_header_by_hash ( const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH : : request & req , COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH : : response & res ) {
2015-07-30 15:22:07 +00:00
Hash block_hash ;
2015-05-27 12:08:46 +00:00
if ( ! parse_hash256 ( req . hash , block_hash ) ) {
throw JsonRpc : : JsonRpcError {
CORE_RPC_ERROR_CODE_WRONG_PARAM ,
" Failed to parse hex representation of block hash. Hex = " + req . hash + ' . ' } ;
}
Block blk ;
2015-07-15 12:23:00 +00:00
if ( ! m_core . getBlockByHash ( block_hash , blk ) ) {
2015-05-27 12:08:46 +00:00
throw JsonRpc : : JsonRpcError {
CORE_RPC_ERROR_CODE_INTERNAL_ERROR ,
" Internal error: can't get block by hash. Hash = " + req . hash + ' . ' } ;
}
2015-07-30 15:22:07 +00:00
if ( blk . baseTransaction . inputs . front ( ) . type ( ) ! = typeid ( BaseInput ) ) {
2015-05-27 12:08:46 +00:00
throw JsonRpc : : JsonRpcError {
CORE_RPC_ERROR_CODE_INTERNAL_ERROR ,
" Internal error: coinbase transaction in the block has the wrong type " } ;
}
2015-07-30 15:22:07 +00:00
uint64_t block_height = boost : : get < BaseInput > ( blk . baseTransaction . inputs . front ( ) ) . blockIndex ;
fill_block_header_response ( blk , false , block_height , block_hash , res . block_header ) ;
2015-05-27 12:08:46 +00:00
res . status = CORE_RPC_STATUS_OK ;
return true ;
}
bool RpcServer : : on_get_block_header_by_height ( const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT : : request & req , COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT : : response & res ) {
if ( m_core . get_current_blockchain_height ( ) < = req . height ) {
throw JsonRpc : : JsonRpcError { CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT ,
std : : string ( " To big height: " ) + std : : to_string ( req . height ) + " , current blockchain height = " + std : : to_string ( m_core . get_current_blockchain_height ( ) ) } ;
}
2015-07-30 15:22:07 +00:00
Hash block_hash = m_core . getBlockIdByHeight ( static_cast < uint32_t > ( req . height ) ) ;
2015-05-27 12:08:46 +00:00
Block blk ;
2015-07-15 12:23:00 +00:00
if ( ! m_core . getBlockByHash ( block_hash , blk ) ) {
2015-05-27 12:08:46 +00:00
throw JsonRpc : : JsonRpcError { CORE_RPC_ERROR_CODE_INTERNAL_ERROR ,
" Internal error: can't get block by height. Height = " + std : : to_string ( req . height ) + ' . ' } ;
}
2015-07-30 15:22:07 +00:00
fill_block_header_response ( blk , false , req . height , block_hash , res . block_header ) ;
2015-05-27 12:08:46 +00:00
res . status = CORE_RPC_STATUS_OK ;
return true ;
}
}