// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2017 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
# include <base58.h>
# include <amount.h>
# include <chain.h>
# include <chainparams.h>
# include <consensus/consensus.h>
# include <consensus/params.h>
# include <consensus/validation.h>
# include <consensus/merkle.h>
# include <core_io.h>
# include <init.h>
# include <validation.h>
# include <miner.h>
# include <net.h>
# include <policy/fees.h>
# include <pow.h>
# include <rpc/blockchain.h>
# include <rpc/mining.h>
# include <rpc/server.h>
# include <txmempool.h>
# include <util.h>
# include <utilstrencodings.h>
# include <validationinterface.h>
# include <warnings.h>
# include <memory>
# include <stdint.h>
# include <cnutils.h>
# include <string_tools.h>
# include <cryptonote_core/cryptonote_tx_utils.h>
# define MAX_RESERVE_SIZE 16
const std : : string CN_DUMMY_ADDRESS = " 45F1UWtrhB33uEQsiyt2DPPcV6BNeBmgH36QcPzNKcKbevZav3gxQzC2LELjQ7CvNMBGGjwHErmhpLWW7okYD4MU1ozsT2C " ;
unsigned int ParseConfirmTarget ( const UniValue & value )
{
int target = value . get_int ( ) ;
unsigned int max_target = : : feeEstimator . HighestTargetTracked ( FeeEstimateHorizon : : LONG_HALFLIFE ) ;
if ( target < 1 | | ( unsigned int ) target > max_target ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , strprintf ( " Invalid conf_target, must be between %u - %u " , 1 , max_target ) ) ;
}
return ( unsigned int ) target ;
}
/**
* Return average network hashes per second based on the last ' lookup ' blocks ,
* or from the last difficulty change if ' lookup ' is nonpositive .
* If ' height ' is nonnegative , compute the estimate at the time when a given block was found .
*/
UniValue GetNetworkHashPS ( int lookup , int height ) {
CBlockIndex * pb = chainActive . Tip ( ) ;
if ( height > = 0 & & height < chainActive . Height ( ) )
pb = chainActive [ height ] ;
if ( pb = = nullptr | | ! pb - > nHeight )
return 0 ;
// If lookup is -1, then use blocks since last difficulty change.
if ( lookup < = 0 )
lookup = pb - > nHeight % Params ( ) . GetConsensus ( ) . DifficultyAdjustmentInterval ( ) + 1 ;
// If lookup is larger than chain, then set it to chain length.
if ( lookup > pb - > nHeight )
lookup = pb - > nHeight ;
CBlockIndex * pb0 = pb ;
int64_t minTime = pb0 - > GetBlockTime ( ) ;
int64_t maxTime = minTime ;
for ( int i = 0 ; i < lookup ; i + + ) {
pb0 = pb0 - > pprev ;
int64_t time = pb0 - > GetBlockTime ( ) ;
minTime = std : : min ( time , minTime ) ;
maxTime = std : : max ( time , maxTime ) ;
}
// In case there's a situation where minTime == maxTime, we don't want a divide by zero exception.
if ( minTime = = maxTime )
return 0 ;
arith_uint256 workDiff = pb - > nChainWork - pb0 - > nChainWork ;
int64_t timeDiff = maxTime - minTime ;
return workDiff . getdouble ( ) / timeDiff ;
}
UniValue getnetworkhashps ( const JSONRPCRequest & request )
{
if ( request . fHelp | | request . params . size ( ) > 2 )
throw std : : runtime_error (
" getnetworkhashps ( nblocks height ) \n "
" \n Returns the estimated network hashes per second based on the last n blocks. \n "
" Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change. \n "
" Pass in [height] to estimate the network speed at the time when a certain block was found. \n "
" \n Arguments: \n "
" 1. nblocks (numeric, optional, default=120) The number of blocks, or -1 for blocks since last difficulty change. \n "
" 2. height (numeric, optional, default=-1) To estimate at the time of the given height. \n "
" \n Result: \n "
" x (numeric) Hashes per second estimated \n "
" \n Examples: \n "
+ HelpExampleCli ( " getnetworkhashps " , " " )
+ HelpExampleRpc ( " getnetworkhashps " , " " )
) ;
LOCK ( cs_main ) ;
return GetNetworkHashPS ( ! request . params [ 0 ] . isNull ( ) ? request . params [ 0 ] . get_int ( ) : 120 , ! request . params [ 1 ] . isNull ( ) ? request . params [ 1 ] . get_int ( ) : - 1 ) ;
}
UniValue generateBlocks ( std : : shared_ptr < CReserveScript > coinbaseScript , int nGenerate , uint64_t nMaxTries , bool keepScript )
{
static const int nInnerLoopCount = 0x10000 ;
int nHeightEnd = 0 ;
int nHeight = 0 ;
{ // Don't keep cs_main locked
LOCK ( cs_main ) ;
nHeight = chainActive . Height ( ) ;
nHeightEnd = nHeight + nGenerate ;
}
unsigned int nExtraNonce = 0 ;
UniValue blockHashes ( UniValue : : VARR ) ;
while ( nHeight < nHeightEnd )
{
std : : unique_ptr < CBlockTemplate > pblocktemplate ( BlockAssembler ( Params ( ) ) . CreateNewBlock ( coinbaseScript - > reserveScript ) ) ;
if ( ! pblocktemplate . get ( ) )
throw JSONRPCError ( RPC_INTERNAL_ERROR , " Couldn't create new block " ) ;
CBlock * pblock = & pblocktemplate - > block ;
{
LOCK ( cs_main ) ;
IncrementExtraNonce ( pblock , chainActive . Tip ( ) , nExtraNonce ) ;
}
pblock - > cnHeader . prev_id = pblock - > GetOriginalBlockHash ( ) ;
while ( nMaxTries > 0 & & pblock - > nNonce < nInnerLoopCount & & ! CheckProofOfWork ( pblock - > GetPoWHash ( ) , pblock - > nBits , Params ( ) . GetConsensus ( ) ) ) {
+ + pblock - > cnHeader . nonce ;
- - nMaxTries ;
}
if ( nMaxTries = = 0 ) {
break ;
}
if ( pblock - > nNonce = = nInnerLoopCount ) {
continue ;
}
std : : shared_ptr < const CBlock > shared_pblock = std : : make_shared < const CBlock > ( * pblock ) ;
if ( ! ProcessNewBlock ( Params ( ) , shared_pblock , true , nullptr ) )
throw JSONRPCError ( RPC_INTERNAL_ERROR , " ProcessNewBlock, block not accepted " ) ;
+ + nHeight ;
blockHashes . push_back ( pblock - > GetHash ( ) . GetHex ( ) ) ;
//mark script as important because it was used at least for one coinbase output if the script came from the wallet
if ( keepScript )
{
coinbaseScript - > KeepScript ( ) ;
}
}
return blockHashes ;
}
UniValue generatetoaddress ( const JSONRPCRequest & request )
{
if ( request . fHelp | | request . params . size ( ) < 2 | | request . params . size ( ) > 3 )
throw std : : runtime_error (
" generatetoaddress nblocks address (maxtries) \n "
" \n Mine blocks immediately to a specified address (before the RPC call returns) \n "
" \n Arguments: \n "
" 1. nblocks (numeric, required) How many blocks are generated immediately. \n "
" 2. address (string, required) The address to send the newly generated kevacoin to. \n "
" 3. maxtries (numeric, optional) How many iterations to try (default = 1000000). \n "
" \n Result: \n "
" [ blockhashes ] (array) hashes of blocks generated \n "
" \n Examples: \n "
" \n Generate 11 blocks to myaddress \n "
+ HelpExampleCli ( " generatetoaddress " , " 11 \" myaddress \" " )
) ;
int nGenerate = request . params [ 0 ] . get_int ( ) ;
uint64_t nMaxTries = 1000000 ;
if ( ! request . params [ 2 ] . isNull ( ) ) {
nMaxTries = request . params [ 2 ] . get_int ( ) ;
}
CTxDestination destination = DecodeDestination ( request . params [ 1 ] . get_str ( ) ) ;
if ( ! IsValidDestination ( destination ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Error: Invalid address " ) ;
}
std : : shared_ptr < CReserveScript > coinbaseScript = std : : make_shared < CReserveScript > ( ) ;
coinbaseScript - > reserveScript = GetScriptForDestination ( destination ) ;
return generateBlocks ( coinbaseScript , nGenerate , nMaxTries , false ) ;
}
UniValue getmininginfo ( const JSONRPCRequest & request )
{
if ( request . fHelp | | request . params . size ( ) ! = 0 )
throw std : : runtime_error (
" getmininginfo \n "
" \n Returns a json object containing mining-related information. "
" \n Result: \n "
" { \n "
" \" blocks \" : nnn, (numeric) The current block \n "
" \" currentblockweight \" : nnn, (numeric) The last block weight \n "
" \" currentblocktx \" : nnn, (numeric) The last block transaction \n "
" \" difficulty \" : xxx.xxxxx (numeric) The current difficulty \n "
" \" networkhashps \" : nnn, (numeric) The network hashes per second \n "
" \" pooledtx \" : n (numeric) The size of the mempool \n "
" \" chain \" : \" xxxx \" , (string) current network name as defined in BIP70 (main, test, regtest) \n "
" \" warnings \" : \" ... \" (string) any network and blockchain warnings \n "
" \" errors \" : \" ... \" (string) DEPRECATED. Same as warnings. Only shown when kevacoind is started with -deprecatedrpc=getmininginfo \n "
" } \n "
" \n Examples: \n "
+ HelpExampleCli ( " getmininginfo " , " " )
+ HelpExampleRpc ( " getmininginfo " , " " )
) ;
LOCK ( cs_main ) ;
UniValue obj ( UniValue : : VOBJ ) ;
obj . push_back ( Pair ( " blocks " , ( int ) chainActive . Height ( ) ) ) ;
obj . push_back ( Pair ( " currentblockweight " , ( uint64_t ) nLastBlockWeight ) ) ;
obj . push_back ( Pair ( " currentblocktx " , ( uint64_t ) nLastBlockTx ) ) ;
obj . push_back ( Pair ( " difficulty " , ( double ) GetDifficulty ( ) ) ) ;
obj . push_back ( Pair ( " networkhashps " , getnetworkhashps ( request ) ) ) ;
obj . push_back ( Pair ( " pooledtx " , ( uint64_t ) mempool . size ( ) ) ) ;
obj . push_back ( Pair ( " chain " , Params ( ) . NetworkIDString ( ) ) ) ;
if ( IsDeprecatedRPCEnabled ( " getmininginfo " ) ) {
obj . push_back ( Pair ( " errors " , GetWarnings ( " statusbar " ) ) ) ;
} else {
obj . push_back ( Pair ( " warnings " , GetWarnings ( " statusbar " ) ) ) ;
}
return obj ;
}
// NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts
UniValue prioritisetransaction ( const JSONRPCRequest & request )
{
if ( request . fHelp | | request . params . size ( ) ! = 3 )
throw std : : runtime_error (
" prioritisetransaction <txid> <dummy value> <fee delta> \n "
" Accepts the transaction into mined blocks at a higher (or lower) priority \n "
" \n Arguments: \n "
" 1. \" txid \" (string, required) The transaction id. \n "
" 2. dummy (numeric, optional) API-Compatibility for previous API. Must be zero or null. \n "
" DEPRECATED. For forward compatibility use named arguments and omit this parameter. \n "
" 3. fee_delta (numeric, required) The fee value (in satoshis) to add (or subtract, if negative). \n "
" The fee is not actually paid, only the algorithm for selecting transactions into a block \n "
" considers the transaction as it would have paid a higher (or lower) fee. \n "
" \n Result: \n "
" true (boolean) Returns true \n "
" \n Examples: \n "
+ HelpExampleCli ( " prioritisetransaction " , " \" txid \" 0.0 10000 " )
+ HelpExampleRpc ( " prioritisetransaction " , " \" txid \" , 0.0, 10000 " )
) ;
LOCK ( cs_main ) ;
uint256 hash = ParseHashStr ( request . params [ 0 ] . get_str ( ) , " txid " ) ;
CAmount nAmount = request . params [ 2 ] . get_int64 ( ) ;
if ( ! ( request . params [ 1 ] . isNull ( ) | | request . params [ 1 ] . get_real ( ) = = 0 ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Priority is no longer supported, dummy argument to prioritisetransaction must be 0. " ) ;
}
mempool . PrioritiseTransaction ( hash , nAmount ) ;
return true ;
}
//---------------------------------------------------------------
// Cryptonote error codes
# define CORE_RPC_ERROR_CODE_WRONG_PARAM -1
# define CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT -2
# define CORE_RPC_ERROR_CODE_TOO_BIG_RESERVE_SIZE -3
# define CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS -4
# define CORE_RPC_ERROR_CODE_INTERNAL_ERROR -5
# define CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB -6
# define CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED -7
# define CORE_RPC_ERROR_CODE_CORE_BUSY -9
# define CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB_SIZE -10
# define CORE_RPC_ERROR_CODE_UNSUPPORTED_RPC -11
# define CORE_RPC_ERROR_CODE_MINING_TO_SUBADDRESS -12
# define CORE_RPC_ERROR_CODE_REGTEST_REQUIRED -13
// Cryptonote error response
UniValue CN_JSONRPCError ( int code , const std : : string & message )
{
UniValue error ( UniValue : : VOBJ ) ;
// Indicate that this is cryptonote error.
error . push_back ( Pair ( " cn_code " , code ) ) ;
error . push_back ( Pair ( " code " , code ) ) ;
error . push_back ( Pair ( " message " , message ) ) ;
return error ;
}
// NOTE: Assumes a conclusive result; if result is inconclusive, it must be handled by caller
static UniValue BIP22ValidationResult ( const CValidationState & state )
{
if ( state . IsValid ( ) )
return NullUniValue ;
std : : string strRejectReason = state . GetRejectReason ( ) ;
if ( state . IsError ( ) )
throw JSONRPCError ( RPC_VERIFY_ERROR , strRejectReason ) ;
if ( state . IsInvalid ( ) )
{
if ( strRejectReason . empty ( ) )
return " rejected " ;
return strRejectReason ;
}
// Should be impossible
return " valid? " ;
}
static UniValue BIP22ValidationResult_CN ( const CValidationState & state )
{
if ( state . IsValid ( ) ) {
UniValue result ( UniValue : : VOBJ ) ;
result . push_back ( Pair ( " status " , " OK " ) ) ;
return result ;
}
std : : string strRejectReason = state . GetRejectReason ( ) ;
if ( state . IsError ( ) )
throw CN_JSONRPCError ( CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED , strRejectReason ) ;
if ( state . IsInvalid ( ) )
{
if ( strRejectReason . empty ( ) )
throw CN_JSONRPCError ( CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED , " rejected " ) ;
throw CN_JSONRPCError ( CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED , strRejectReason ) ;
}
// Should be impossible
throw CN_JSONRPCError ( CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED , " valid? " ) ;
}
std : : string gbt_vb_name ( const Consensus : : DeploymentPos pos ) {
const struct VBDeploymentInfo & vbinfo = VersionBitsDeploymentInfo [ pos ] ;
std : : string s = vbinfo . name ;
if ( ! vbinfo . gbt_force ) {
s . insert ( s . begin ( ) , ' ! ' ) ;
}
return s ;
}
UniValue getblocktemplate_original ( const JSONRPCRequest & request )
{
if ( request . fHelp | | request . params . size ( ) > 1 )
throw std : : runtime_error (
" getblocktemplate ( TemplateRequest ) \n "
" \n If the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'. \n "
" It returns data needed to construct a block to work on. \n "
" For full specification, see BIPs 22, 23, 9, and 145: \n "
" https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki \n "
" https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki \n "
" https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes \n "
" https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki \n "
" \n Arguments: \n "
" 1. template_request (json object, optional) A json object in the following spec \n "
" { \n "
" \" mode \" : \" template \" (string, optional) This must be set to \" template \" , \" proposal \" (see BIP 23), or omitted \n "
" \" capabilities \" :[ (array, optional) A list of strings \n "
" \" support \" (string) client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid' \n "
" ,... \n "
" ], \n "
" \" rules \" :[ (array, optional) A list of strings \n "
" \" support \" (string) client side supported softfork deployment \n "
" ,... \n "
" ] \n "
" } \n "
" \n "
" \n Result: \n "
" { \n "
" \" version \" : n, (numeric) The preferred block version \n "
" \" rules \" : [ \" rulename \" , ... ], (array of strings) specific block rules that are to be enforced \n "
" \" vbavailable \" : { (json object) set of pending, supported versionbit (BIP 9) softfork deployments \n "
" \" rulename \" : bitnumber (numeric) identifies the bit number as indicating acceptance and readiness for the named softfork rule \n "
" ,... \n "
" }, \n "
" \" vbrequired \" : n, (numeric) bit mask of versionbits the server requires set in submissions \n "
" \" previousblockhash \" : \" xxxx \" , (string) The hash of current highest block \n "
" \" transactions \" : [ (array) contents of non-coinbase transactions that should be included in the next block \n "
" { \n "
" \" data \" : \" xxxx \" , (string) transaction data encoded in hexadecimal (byte-for-byte) \n "
" \" txid \" : \" xxxx \" , (string) transaction id encoded in little-endian hexadecimal \n "
" \" hash \" : \" xxxx \" , (string) hash encoded in little-endian hexadecimal (including witness data) \n "
" \" depends \" : [ (array) array of numbers \n "
" n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is \n "
" ,... \n "
" ], \n "
" \" fee \" : n, (numeric) difference in value between transaction inputs and outputs (in satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one \n "
" \" sigops \" : n, (numeric) total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero \n "
" \" weight \" : n, (numeric) total transaction weight, as counted for purposes of block limits \n "
" \" required \" : true|false (boolean) if provided and true, this transaction must be in the final block \n "
" } \n "
" ,... \n "
" ], \n "
" \" coinbaseaux \" : { (json object) data that should be included in the coinbase's scriptSig content \n "
" \" flags \" : \" xx \" (string) key name is to be ignored, and value included in scriptSig \n "
" }, \n "
" \" coinbasevalue \" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis) \n "
" \" coinbasetxn \" : { ... }, (json object) information for coinbase transaction \n "
" \" target \" : \" xxxx \" , (string) The hash target \n "
" \" mintime \" : xxx, (numeric) The minimum timestamp appropriate for next block time in seconds since epoch (Jan 1 1970 GMT) \n "
" \" mutable \" : [ (array of string) list of ways the block template may be changed \n "
" \" value \" (string) A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock' \n "
" ,... \n "
" ], \n "
" \" noncerange \" : \" 00000000ffffffff \" ,(string) A range of valid nonces \n "
" \" sigoplimit \" : n, (numeric) limit of sigops in blocks \n "
" \" sizelimit \" : n, (numeric) limit of block size \n "
" \" weightlimit \" : n, (numeric) limit of block weight \n "
" \" curtime \" : ttt, (numeric) current timestamp in seconds since epoch (Jan 1 1970 GMT) \n "
" \" bits \" : \" xxxxxxxx \" , (string) compressed target of next block \n "
" \" height \" : n (numeric) The height of the next block \n "
" } \n "
" \n Examples: \n "
+ HelpExampleCli ( " getblocktemplate " , " " )
+ HelpExampleRpc ( " getblocktemplate " , " " )
) ;
LOCK ( cs_main ) ;
std : : string strMode = " template " ;
UniValue lpval = NullUniValue ;
std : : set < std : : string > setClientRules ;
int64_t nMaxVersionPreVB = - 1 ;
if ( ! request . params [ 0 ] . isNull ( ) )
{
const UniValue & oparam = request . params [ 0 ] . get_obj ( ) ;
const UniValue & modeval = find_value ( oparam , " mode " ) ;
if ( modeval . isStr ( ) )
strMode = modeval . get_str ( ) ;
else if ( modeval . isNull ( ) )
{
/* Do nothing */
}
else
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Invalid mode " ) ;
lpval = find_value ( oparam , " longpollid " ) ;
if ( strMode = = " proposal " )
{
const UniValue & dataval = find_value ( oparam , " data " ) ;
if ( ! dataval . isStr ( ) )
throw JSONRPCError ( RPC_TYPE_ERROR , " Missing data String key for proposal " ) ;
CBlock block ;
if ( ! DecodeHexBlk ( block , dataval . get_str ( ) ) )
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , " Block decode failed " ) ;
uint256 hash = block . GetHash ( ) ;
BlockMap : : iterator mi = mapBlockIndex . find ( hash ) ;
if ( mi ! = mapBlockIndex . end ( ) ) {
CBlockIndex * pindex = mi - > second ;
if ( pindex - > IsValid ( BLOCK_VALID_SCRIPTS ) )
return " duplicate " ;
if ( pindex - > nStatus & BLOCK_FAILED_MASK )
return " duplicate-invalid " ;
return " duplicate-inconclusive " ;
}
CBlockIndex * const pindexPrev = chainActive . Tip ( ) ;
// TestBlockValidity only supports blocks built on the current Tip
if ( block . hashPrevBlock ! = pindexPrev - > GetBlockHash ( ) )
return " inconclusive-not-best-prevblk " ;
CValidationState state ;
TestBlockValidity ( state , Params ( ) , block , pindexPrev , false , true ) ;
return BIP22ValidationResult ( state ) ;
}
const UniValue & aClientRules = find_value ( oparam , " rules " ) ;
if ( aClientRules . isArray ( ) ) {
for ( unsigned int i = 0 ; i < aClientRules . size ( ) ; + + i ) {
const UniValue & v = aClientRules [ i ] ;
setClientRules . insert ( v . get_str ( ) ) ;
}
} else {
// NOTE: It is important that this NOT be read if versionbits is supported
const UniValue & uvMaxVersion = find_value ( oparam , " maxversion " ) ;
if ( uvMaxVersion . isNum ( ) ) {
nMaxVersionPreVB = uvMaxVersion . get_int64 ( ) ;
}
}
}
if ( strMode ! = " template " )
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Invalid mode " ) ;
if ( ! g_connman )
throw JSONRPCError ( RPC_CLIENT_P2P_DISABLED , " Error: Peer-to-peer functionality missing or disabled " ) ;
if ( g_connman - > GetNodeCount ( CConnman : : CONNECTIONS_ALL ) = = 0 )
throw JSONRPCError ( RPC_CLIENT_NOT_CONNECTED , " Kevacoin is not connected! " ) ;
if ( IsInitialBlockDownload ( ) )
throw JSONRPCError ( RPC_CLIENT_IN_INITIAL_DOWNLOAD , " Kevacoin is downloading blocks... " ) ;
static unsigned int nTransactionsUpdatedLast ;
if ( ! lpval . isNull ( ) )
{
// Wait to respond until either the best block changes, OR a minute has passed and there are more transactions
uint256 hashWatchedChain ;
std : : chrono : : steady_clock : : time_point checktxtime ;
unsigned int nTransactionsUpdatedLastLP ;
if ( lpval . isStr ( ) )
{
// Format: <hashBestChain><nTransactionsUpdatedLast>
std : : string lpstr = lpval . get_str ( ) ;
hashWatchedChain . SetHex ( lpstr . substr ( 0 , 64 ) ) ;
nTransactionsUpdatedLastLP = atoi64 ( lpstr . substr ( 64 ) ) ;
}
else
{
// NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier
hashWatchedChain = chainActive . Tip ( ) - > GetBlockHash ( ) ;
nTransactionsUpdatedLastLP = nTransactionsUpdatedLast ;
}
// Release the wallet and main lock while waiting
LEAVE_CRITICAL_SECTION ( cs_main ) ;
{
checktxtime = std : : chrono : : steady_clock : : now ( ) + std : : chrono : : minutes ( 1 ) ;
WaitableLock lock ( csBestBlock ) ;
while ( hashBestBlock = = hashWatchedChain & & IsRPCRunning ( ) )
{
if ( cvBlockChange . wait_until ( lock , checktxtime ) = = std : : cv_status : : timeout )
{
// Timeout: Check transactions for update
if ( mempool . GetTransactionsUpdated ( ) ! = nTransactionsUpdatedLastLP )
break ;
checktxtime + = std : : chrono : : seconds ( 10 ) ;
}
}
}
ENTER_CRITICAL_SECTION ( cs_main ) ;
if ( ! IsRPCRunning ( ) )
throw JSONRPCError ( RPC_CLIENT_NOT_CONNECTED , " Shutting down " ) ;
// TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners?
}
const struct VBDeploymentInfo & segwit_info = VersionBitsDeploymentInfo [ Consensus : : DEPLOYMENT_SEGWIT ] ;
// If the caller is indicating segwit support, then allow CreateNewBlock()
// to select witness transactions, after segwit activates (otherwise
// don't).
bool fSupportsSegwit = setClientRules . find ( segwit_info . name ) ! = setClientRules . end ( ) ;
// Update block
static CBlockIndex * pindexPrev ;
static int64_t nStart ;
static std : : unique_ptr < CBlockTemplate > pblocktemplate ;
// Cache whether the last invocation was with segwit support, to avoid returning
// a segwit-block to a non-segwit caller.
static bool fLastTemplateSupportsSegwit = true ;
if ( pindexPrev ! = chainActive . Tip ( ) | |
( mempool . GetTransactionsUpdated ( ) ! = nTransactionsUpdatedLast & & GetTime ( ) - nStart > 5 ) | |
fLastTemplateSupportsSegwit ! = fSupportsSegwit )
{
// Clear pindexPrev so future calls make a new block, despite any failures from here on
pindexPrev = nullptr ;
// Store the pindexBest used before CreateNewBlock, to avoid races
nTransactionsUpdatedLast = mempool . GetTransactionsUpdated ( ) ;
CBlockIndex * pindexPrevNew = chainActive . Tip ( ) ;
nStart = GetTime ( ) ;
fLastTemplateSupportsSegwit = fSupportsSegwit ;
// Create new block
CScript scriptDummy = CScript ( ) < < OP_TRUE ;
pblocktemplate = BlockAssembler ( Params ( ) ) . CreateNewBlock ( scriptDummy , fSupportsSegwit ) ;
if ( ! pblocktemplate )
throw JSONRPCError ( RPC_OUT_OF_MEMORY , " Out of memory " ) ;
// Need to update only after we know CreateNewBlock succeeded
pindexPrev = pindexPrevNew ;
}
CBlock * pblock = & pblocktemplate - > block ; // pointer for convenience
const Consensus : : Params & consensusParams = Params ( ) . GetConsensus ( ) ;
// Update nTime
UpdateTime ( pblock , consensusParams , pindexPrev ) ;
pblock - > nNonce = 0 ;
// NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration
const bool fPreSegWit = ( THRESHOLD_ACTIVE ! = VersionBitsState ( pindexPrev , consensusParams , Consensus : : DEPLOYMENT_SEGWIT , versionbitscache ) ) ;
UniValue aCaps ( UniValue : : VARR ) ; aCaps . push_back ( " proposal " ) ;
UniValue transactions ( UniValue : : VARR ) ;
std : : map < uint256 , int64_t > setTxIndex ;
int i = 0 ;
for ( const auto & it : pblock - > vtx ) {
const CTransaction & tx = * it ;
uint256 txHash = tx . GetHash ( ) ;
setTxIndex [ txHash ] = i + + ;
if ( tx . IsCoinBase ( ) )
continue ;
UniValue entry ( UniValue : : VOBJ ) ;
entry . push_back ( Pair ( " data " , EncodeHexTx ( tx ) ) ) ;
entry . push_back ( Pair ( " txid " , txHash . GetHex ( ) ) ) ;
entry . push_back ( Pair ( " hash " , tx . GetWitnessHash ( ) . GetHex ( ) ) ) ;
UniValue deps ( UniValue : : VARR ) ;
for ( const CTxIn & in : tx . vin )
{
if ( setTxIndex . count ( in . prevout . hash ) )
deps . push_back ( setTxIndex [ in . prevout . hash ] ) ;
}
entry . push_back ( Pair ( " depends " , deps ) ) ;
int index_in_template = i - 1 ;
entry . push_back ( Pair ( " fee " , pblocktemplate - > vTxFees [ index_in_template ] ) ) ;
int64_t nTxSigOps = pblocktemplate - > vTxSigOpsCost [ index_in_template ] ;
if ( fPreSegWit ) {
assert ( nTxSigOps % WITNESS_SCALE_FACTOR = = 0 ) ;
nTxSigOps / = WITNESS_SCALE_FACTOR ;
}
entry . push_back ( Pair ( " sigops " , nTxSigOps ) ) ;
entry . push_back ( Pair ( " weight " , GetTransactionWeight ( tx ) ) ) ;
transactions . push_back ( entry ) ;
}
UniValue aux ( UniValue : : VOBJ ) ;
aux . push_back ( Pair ( " flags " , HexStr ( COINBASE_FLAGS . begin ( ) , COINBASE_FLAGS . end ( ) ) ) ) ;
arith_uint256 hashTarget = arith_uint256 ( ) . SetCompact ( pblock - > nBits ) ;
UniValue aMutable ( UniValue : : VARR ) ;
aMutable . push_back ( " time " ) ;
aMutable . push_back ( " transactions " ) ;
aMutable . push_back ( " prevblock " ) ;
UniValue result ( UniValue : : VOBJ ) ;
result . push_back ( Pair ( " capabilities " , aCaps ) ) ;
UniValue aRules ( UniValue : : VARR ) ;
UniValue vbavailable ( UniValue : : VOBJ ) ;
for ( int j = 0 ; j < ( int ) Consensus : : MAX_VERSION_BITS_DEPLOYMENTS ; + + j ) {
Consensus : : DeploymentPos pos = Consensus : : DeploymentPos ( j ) ;
ThresholdState state = VersionBitsState ( pindexPrev , consensusParams , pos , versionbitscache ) ;
switch ( state ) {
case THRESHOLD_DEFINED :
case THRESHOLD_FAILED :
// Not exposed to GBT at all
break ;
case THRESHOLD_LOCKED_IN :
// Ensure bit is set in block version
pblock - > nVersion | = VersionBitsMask ( consensusParams , pos ) ;
// FALL THROUGH to get vbavailable set...
case THRESHOLD_STARTED :
{
const struct VBDeploymentInfo & vbinfo = VersionBitsDeploymentInfo [ pos ] ;
vbavailable . push_back ( Pair ( gbt_vb_name ( pos ) , consensusParams . vDeployments [ pos ] . bit ) ) ;
if ( setClientRules . find ( vbinfo . name ) = = setClientRules . end ( ) ) {
if ( ! vbinfo . gbt_force ) {
// If the client doesn't support this, don't indicate it in the [default] version
pblock - > nVersion & = ~ VersionBitsMask ( consensusParams , pos ) ;
}
}
break ;
}
case THRESHOLD_ACTIVE :
{
// Add to rules only
const struct VBDeploymentInfo & vbinfo = VersionBitsDeploymentInfo [ pos ] ;
aRules . push_back ( gbt_vb_name ( pos ) ) ;
if ( setClientRules . find ( vbinfo . name ) = = setClientRules . end ( ) ) {
// Not supported by the client; make sure it's safe to proceed
if ( ! vbinfo . gbt_force ) {
// If we do anything other than throw an exception here, be sure version/force isn't sent to old clients
throw JSONRPCError ( RPC_INVALID_PARAMETER , strprintf ( " Support for '%s' rule requires explicit client support " , vbinfo . name ) ) ;
}
}
break ;
}
}
}
result . push_back ( Pair ( " version " , pblock - > nVersion ) ) ;
result . push_back ( Pair ( " rules " , aRules ) ) ;
result . push_back ( Pair ( " vbavailable " , vbavailable ) ) ;
result . push_back ( Pair ( " vbrequired " , int ( 0 ) ) ) ;
if ( nMaxVersionPreVB > = 2 ) {
// If VB is supported by the client, nMaxVersionPreVB is -1, so we won't get here
// Because BIP 34 changed how the generation transaction is serialized, we can only use version/force back to v2 blocks
// This is safe to do [otherwise-]unconditionally only because we are throwing an exception above if a non-force deployment gets activated
// Note that this can probably also be removed entirely after the first BIP9 non-force deployment (ie, probably segwit) gets activated
aMutable . push_back ( " version/force " ) ;
}
result . push_back ( Pair ( " previousblockhash " , pblock - > hashPrevBlock . GetHex ( ) ) ) ;
result . push_back ( Pair ( " transactions " , transactions ) ) ;
result . push_back ( Pair ( " coinbaseaux " , aux ) ) ;
result . push_back ( Pair ( " coinbasevalue " , ( int64_t ) pblock - > vtx [ 0 ] - > vout [ 0 ] . nValue ) ) ;
result . push_back ( Pair ( " longpollid " , chainActive . Tip ( ) - > GetBlockHash ( ) . GetHex ( ) + i64tostr ( nTransactionsUpdatedLast ) ) ) ;
result . push_back ( Pair ( " target " , hashTarget . GetHex ( ) ) ) ;
result . push_back ( Pair ( " mintime " , ( int64_t ) pindexPrev - > GetMedianTimePast ( ) + 1 ) ) ;
result . push_back ( Pair ( " mutable " , aMutable ) ) ;
result . push_back ( Pair ( " noncerange " , " 00000000ffffffff " ) ) ;
int64_t nSigOpLimit = MAX_BLOCK_SIGOPS_COST ;
int64_t nSizeLimit = MAX_BLOCK_SERIALIZED_SIZE ;
if ( fPreSegWit ) {
assert ( nSigOpLimit % WITNESS_SCALE_FACTOR = = 0 ) ;
nSigOpLimit / = WITNESS_SCALE_FACTOR ;
assert ( nSizeLimit % WITNESS_SCALE_FACTOR = = 0 ) ;
nSizeLimit / = WITNESS_SCALE_FACTOR ;
}
result . push_back ( Pair ( " sigoplimit " , nSigOpLimit ) ) ;
result . push_back ( Pair ( " sizelimit " , nSizeLimit ) ) ;
if ( ! fPreSegWit ) {
result . push_back ( Pair ( " weightlimit " , ( int64_t ) MAX_BLOCK_WEIGHT ) ) ;
}
result . push_back ( Pair ( " curtime " , pblock - > GetBlockTime ( ) ) ) ;
result . push_back ( Pair ( " bits " , strprintf ( " %08x " , pblock - > nBits ) ) ) ;
result . push_back ( Pair ( " height " , ( int64_t ) ( pindexPrev - > nHeight + 1 ) ) ) ;
if ( ! pblocktemplate - > vchCoinbaseCommitment . empty ( ) & & fSupportsSegwit ) {
result . push_back ( Pair ( " default_witness_commitment " , HexStr ( pblocktemplate - > vchCoinbaseCommitment . begin ( ) , pblocktemplate - > vchCoinbaseCommitment . end ( ) ) ) ) ;
}
return result ;
}
// Cryptonote RPC API. Not to be confused with the Bitcoin imlementation (see getblocktemplate_original).
UniValue getblocktemplate ( const JSONRPCRequest & request )
{
// JSON-RPC2 request
// {reserve_size: 8, wallet_address: poolAddress}
if ( request . fHelp | | request . params . size ( ) ! = 2 )
throw std : : runtime_error (
" getblocktemplate ( TemplateRequest ) \n "
" \n This is a Cryptonote API and is not compatible with original bitcoin implementation (See getblocktemplate_original). \n "
" Get a block template on which mining a new block. \n "
" \n Arguments: \n "
" 1. reserve_size (unsigned int) Reserve size \n "
" 2. wallet_address (string) Address of wallet to receive coinbase transactions if block is successfully mined. \n "
" \n Result: \n "
" { \n "
" \" blocktemplate_blob \" : \" xxxx \" , (string) Blob on which to try to mine a new block. \n "
" \" blockhashing_blob \" : \" xxxx \" , (string) Blob on which to try to find a valid nonce. \n "
" \" difficulty \" : n, (unsigned int) Difficulty of next block. \n "
" \" expected_reward \" : n, (unsigned int) Coinbase reward expected to be received if block is successfully mined. \n "
" \" height \" : n, (unsigned int) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis) \n "
" \" prev_hash \" : \" xxxx \" , (string) Hash of the most recent block on which to mine the next block. \n "
" \" reserved_offset \" : \" xxxx \" , (unsigned int) Reserved offset. \n "
" \" status \" : \" xxx \" , (string) General RPC error code. \" OK \" means everything looks good. \n "
" \" untrusted \" : n, (boolean) States if the result is obtained using the bootstrap mode, and is therefore not trusted (true), or when the daemon is fully synced (false). \n "
" } \n "
" \n Examples: \n "
+ HelpExampleCli ( " getblocktemplate " , " 8 TGcYLSTvYRy9uaqEZMfs2i1fXdfourYXDb " )
) ;
int reserve_size ;
std : : string wallet_address ;
CTxDestination walletDest ;
// reserve_size
if ( request . params [ 0 ] . getType ( ) ! = UniValue : : VNUM ) {
throw CN_JSONRPCError ( CORE_RPC_ERROR_CODE_WRONG_PARAM , " reserve_size must be an integer " ) ;
}
reserve_size = request . params [ 0 ] . get_int ( ) ;
if ( reserve_size < = 0 | | reserve_size > MAX_RESERVE_SIZE ) {
throw CN_JSONRPCError ( CORE_RPC_ERROR_CODE_WRONG_PARAM , " Invalid reserve_size " ) ;
}
// wallet_address
if ( request . params [ 1 ] . getType ( ) ! = UniValue : : VSTR ) {
throw CN_JSONRPCError ( CORE_RPC_ERROR_CODE_WRONG_PARAM , " Invalid wallet address, string expected " ) ;
}
wallet_address = request . params [ 1 ] . get_str ( ) ;
walletDest = DecodeDestination ( wallet_address ) ;
if ( walletDest . which ( ) = = 0 ) {
throw CN_JSONRPCError ( CORE_RPC_ERROR_CODE_WRONG_PARAM , " Invalid wallet address " ) ;
}
LOCK ( cs_main ) ;
std : : string strMode = " template " ;
if ( g_connman - > GetNodeCount ( CConnman : : CONNECTIONS_ALL ) = = 0 )
throw CN_JSONRPCError ( CORE_RPC_ERROR_CODE_CORE_BUSY , " Kevacoin is not connected! " ) ;
if ( IsInitialBlockDownload ( ) )
throw CN_JSONRPCError ( CORE_RPC_ERROR_CODE_CORE_BUSY , " Kevacoin is downloading blocks... " ) ;
static unsigned int nTransactionsUpdatedLast ;
bool fSupportsSegwit = true ;
// Update block
static CBlockIndex * pindexPrev ;
static int64_t nStart ;
static std : : unique_ptr < CBlockTemplate > pblocktemplate ;
if ( pindexPrev ! = chainActive . Tip ( ) | |
( mempool . GetTransactionsUpdated ( ) ! = nTransactionsUpdatedLast & & GetTime ( ) - nStart > 5 ) )
{
// Clear pindexPrev so future calls make a new block, despite any failures from here on
pindexPrev = nullptr ;
// Store the pindexBest used before CreateNewBlock, to avoid races
nTransactionsUpdatedLast = mempool . GetTransactionsUpdated ( ) ;
CBlockIndex * pindexPrevNew = chainActive . Tip ( ) ;
nStart = GetTime ( ) ;
// Create new block
CScript scriptPubKey = GetScriptForDestination ( walletDest ) ;
pblocktemplate = BlockAssembler ( Params ( ) ) . CreateNewBlock ( scriptPubKey , fSupportsSegwit ) ;
if ( ! pblocktemplate )
throw JSONRPCError ( RPC_OUT_OF_MEMORY , " Out of memory " ) ;
// Need to update only after we know CreateNewBlock succeeded
pindexPrev = pindexPrevNew ;
}
CBlock * pblock = & pblocktemplate - > block ; // pointer for convenience
const Consensus : : Params & consensusParams = Params ( ) . GetConsensus ( ) ;
// Update nTime
UpdateTime ( pblock , consensusParams , pindexPrev ) ;
std : : set < std : : string > setClientRules ;
UniValue aRules ( UniValue : : VARR ) ;
UniValue vbavailable ( UniValue : : VOBJ ) ;
for ( int j = 0 ; j < ( int ) Consensus : : MAX_VERSION_BITS_DEPLOYMENTS ; + + j ) {
Consensus : : DeploymentPos pos = Consensus : : DeploymentPos ( j ) ;
ThresholdState state = VersionBitsState ( pindexPrev , consensusParams , pos , versionbitscache ) ;
switch ( state ) {
case THRESHOLD_DEFINED :
case THRESHOLD_FAILED :
// Not exposed to GBT at all
break ;
case THRESHOLD_LOCKED_IN :
// Ensure bit is set in block version
pblock - > nVersion | = VersionBitsMask ( consensusParams , pos ) ;
// FALL THROUGH to get vbavailable set...
case THRESHOLD_STARTED :
{
const struct VBDeploymentInfo & vbinfo = VersionBitsDeploymentInfo [ pos ] ;
vbavailable . push_back ( Pair ( gbt_vb_name ( pos ) , consensusParams . vDeployments [ pos ] . bit ) ) ;
if ( setClientRules . find ( vbinfo . name ) = = setClientRules . end ( ) ) {
if ( ! vbinfo . gbt_force ) {
// If the client doesn't support this, don't indicate it in the [default] version
pblock - > nVersion & = ~ VersionBitsMask ( consensusParams , pos ) ;
}
}
break ;
}
case THRESHOLD_ACTIVE :
{
// Add to rules only
const struct VBDeploymentInfo & vbinfo = VersionBitsDeploymentInfo [ pos ] ;
aRules . push_back ( gbt_vb_name ( pos ) ) ;
if ( setClientRules . find ( vbinfo . name ) = = setClientRules . end ( ) ) {
// Not supported by the client; make sure it's safe to proceed
if ( ! vbinfo . gbt_force ) {
// If we do anything other than throw an exception here, be sure version/force isn't sent to old clients
throw JSONRPCError ( RPC_INVALID_PARAMETER , strprintf ( " Support for '%s' rule requires explicit client support " , vbinfo . name ) ) ;
}
}
break ;
}
}
}
// Generate the merkle root as all the transactions (including coinbase) are known.
pblock - > hashMerkleRoot = BlockMerkleRoot ( * pblock ) ;
uint256 blockHash = pblock - > GetOriginalBlockHash ( ) ;
cryptonote : : block cn_block ;
// block_header
cn_block . major_version = consensusParams . GetCryptonoteMajorVersion ( pblock - > nNonce ) ;
cn_block . minor_version = 0 ;
cn_block . timestamp = pblock - > GetBlockTime ( ) ;
// The prev_id is used to store kevacoin block hash, as a proof of work.
memcpy ( & ( cn_block . prev_id ) , blockHash . begin ( ) , blockHash . size ( ) ) ;
cn_block . nonce = 0 ;
// block
// Coinbase transaction.
const uint32_t height = pindexPrev - > nHeight + 1 ;
const size_t miner_height = 10000 ;
const size_t median_size = 20000 ;
const uint64_t already_generated_coins = 10000 ;
const size_t current_block_size = 20000 ;
const uint64_t fee = 0 ;
const size_t max_outs = 10 ;
cryptonote : : account_public_address miner_address ;
cryptonote : : address_parse_info parserInfo ;
cryptonote : : transaction miner_tx ;
cryptonote : : blobdata extra_nonce ;
// The reserve_offset is the offset for extra_nonce
extra_nonce . resize ( reserve_size , 0 ) ;
if ( ! cryptonote : : get_account_address_from_str ( parserInfo , cryptonote : : MAINNET , CN_DUMMY_ADDRESS ) ) {
throw JSONRPCError ( RPC_INTERNAL_ERROR , " Internal error: failed to parse wallet address " ) ;
}
miner_address = parserInfo . address ;
if ( ! construct_miner_tx ( miner_height , median_size , already_generated_coins , current_block_size ,
fee , miner_address , miner_tx , extra_nonce , max_outs ) ) {
throw JSONRPCError ( RPC_INTERNAL_ERROR , " Internal error: failed to construct miner tx " ) ;
}
cn_block . miner_tx = miner_tx ;
// No transactions other than coinbase.
cn_block . tx_hashes . clear ( ) ;
// Copy keva block to extra so that we can use it in submitblock.
CDataStream stream ( SER_NETWORK , PROTOCOL_VERSION ) ;
stream < < * pblock ;
std : : string kevaBlockData = stream . str ( ) ;
cryptonote : : tx_extra_keva_block extra_keva_block ;
extra_keva_block . keva_block = kevaBlockData ;
if ( ! cryptonote : : append_keva_block_to_extra ( cn_block . miner_tx . extra , extra_keva_block ) ) {
throw JSONRPCError ( RPC_INTERNAL_ERROR , " Internal error: failed to add block " ) ;
}
// Now cn_block is finalized, we can calculate the reserved offset position.
cryptonote : : blobdata block_blob = cryptonote : : t_serializable_object_to_blob ( cn_block ) ;
crypto : : public_key tx_pub_key = cryptonote : : get_tx_pub_key_from_extra ( cn_block . miner_tx ) ;
if ( tx_pub_key = = crypto : : null_pkey ) {
throw JSONRPCError ( RPC_INTERNAL_ERROR , " Internal error: failed to tx pub key in coinbase extra " ) ;
}
uint32_t reserved_offset = slow_memmem ( ( void * ) block_blob . data ( ) , block_blob . size ( ) , & tx_pub_key , sizeof ( tx_pub_key ) ) ;
if ( ! reserved_offset ) {
throw JSONRPCError ( RPC_INTERNAL_ERROR , " Internal error: Failed to find tx pub key in blockblob " ) ;
}
reserved_offset + = sizeof ( tx_pub_key ) + 2 ; //2 bytes: tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte)
if ( reserved_offset + reserve_size > block_blob . size ( ) )
{
throw JSONRPCError ( RPC_INTERNAL_ERROR , " Internal error: Failed to calculate offset " ) ;
}
std : : string hex_template_blob = HexStr ( block_blob . begin ( ) , block_blob . end ( ) ) ;
UniValue result ( UniValue : : VOBJ ) ;
const uint64_t difficulty = ConvertNBitsToDiffU64 ( pblock - > nBits ) ;
result . push_back ( Pair ( " blocktemplate_blob " , hex_template_blob ) ) ;
result . push_back ( Pair ( " difficulty " , ( double ) difficulty ) ) ;
result . push_back ( Pair ( " height " , ( uint64_t ) height ) ) ;
result . push_back ( Pair ( " prev_hash " , pblock - > hashPrevBlock . GetHex ( ) ) ) ;
result . push_back ( Pair ( " reserved_offset " , ( uint64_t ) reserved_offset ) ) ;
if ( cn_block . major_version > = RX_BLOCK_VERSION ) {
uint64_t seed_height , next_height ;
crypto : : rx_seedheights ( height , & seed_height , & next_height ) ;
result . push_back ( Pair ( " seed_hash " , chainActive [ seed_height ] - > GetBlockHash ( ) . GetHex ( ) ) ) ;
result . push_back ( Pair ( " next_seed_hash " , chainActive [ next_height ] - > GetBlockHash ( ) . GetHex ( ) ) ) ;
}
// Kevacoin specific entries. Not used for now and may be useful in the future.
result . push_back ( Pair ( " rules " , aRules ) ) ;
result . push_back ( Pair ( " vbavailable " , vbavailable ) ) ;
return result ;
}
class submitblock_StateCatcher : public CValidationInterface
{
public :
uint256 hash ;
bool found ;
CValidationState state ;
explicit submitblock_StateCatcher ( const uint256 & hashIn ) : hash ( hashIn ) , found ( false ) , state ( ) { }
protected :
void BlockChecked ( const CBlock & block , const CValidationState & stateIn ) override {
if ( block . GetHash ( ) ! = hash )
return ;
found = true ;
state = stateIn ;
}
} ;
static uint256 CryptoHashToUint256 ( const crypto : : hash & hash )
{
std : : vector < unsigned char > prev_id ( ( unsigned char * ) ( & hash ) , ( unsigned char * ) & ( hash ) + sizeof ( crypto : : hash ) ) ;
return uint256 ( prev_id ) ;
}
UniValue submitblock_original ( const JSONRPCRequest & request )
{
// We allow 2 arguments for compliance with BIP22. Argument 2 is ignored.
if ( request . fHelp | | request . params . size ( ) < 1 | | request . params . size ( ) > 2 ) {
throw std : : runtime_error (
" submitblock \" hexdata \" ( \" dummy \" ) \n "
" \n Attempts to submit new block to network. \n "
" See https://en.bitcoin.it/wiki/BIP_0022 for full specification. \n "
" \n Arguments \n "
" 1. \" hexdata \" (string, required) the hex-encoded block data to submit \n "
" 2. \" dummy \" (optional) dummy value, for compatibility with BIP22. This value is ignored. \n "
" \n Result: \n "
" \n Examples: \n "
+ HelpExampleCli ( " submitblock " , " \" mydata \" " )
+ HelpExampleRpc ( " submitblock " , " \" mydata \" " )
) ;
}
std : : shared_ptr < CBlock > blockptr = std : : make_shared < CBlock > ( ) ;
CBlock & block = * blockptr ;
if ( ! DecodeHexBlk ( block , request . params [ 0 ] . get_str ( ) ) ) {
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , " Block decode failed " ) ;
}
if ( block . vtx . empty ( ) | | ! block . vtx [ 0 ] - > IsCoinBase ( ) ) {
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , " Block does not start with a coinbase " ) ;
}
uint256 hash = block . GetHash ( ) ;
bool fBlockPresent = false ;
{
LOCK ( cs_main ) ;
BlockMap : : iterator mi = mapBlockIndex . find ( hash ) ;
if ( mi ! = mapBlockIndex . end ( ) ) {
CBlockIndex * pindex = mi - > second ;
if ( pindex - > IsValid ( BLOCK_VALID_SCRIPTS ) ) {
return " duplicate " ;
}
if ( pindex - > nStatus & BLOCK_FAILED_MASK ) {
return " duplicate-invalid " ;
}
// Otherwise, we might only have the header - process the block before returning
fBlockPresent = true ;
}
}
{
LOCK ( cs_main ) ;
BlockMap : : iterator mi = mapBlockIndex . find ( block . hashPrevBlock ) ;
if ( mi ! = mapBlockIndex . end ( ) ) {
UpdateUncommittedBlockStructures ( block , mi - > second , Params ( ) . GetConsensus ( ) ) ;
}
}
submitblock_StateCatcher sc ( block . GetHash ( ) ) ;
RegisterValidationInterface ( & sc ) ;
bool fAccepted = ProcessNewBlock ( Params ( ) , blockptr , true , nullptr ) ;
UnregisterValidationInterface ( & sc ) ;
if ( fBlockPresent ) {
if ( fAccepted & & ! sc . found ) {
return " duplicate-inconclusive " ;
}
return " duplicate " ;
}
if ( ! sc . found ) {
return " inconclusive " ;
}
return BIP22ValidationResult ( sc . state ) ;
}
// Cryptonote RPC call
UniValue submitblock ( const JSONRPCRequest & request )
{
if ( request . fHelp | | request . params . size ( ) ! = 1 ) {
throw std : : runtime_error (
" submitblock \" hexdata \" \n "
" \n This is a Cryptonote API and is not compatible with original bitcoin implementation (See submitblock_original). \n "
" \n Attempts to submit new block to network. \n "
" \n Arguments \n "
" 1. \" hexdata \" (string, required) the hex-encoded block data to submit \n "
" \n Result: \n "
" \n Examples: \n "
+ HelpExampleCli ( " submitblock " , " \" mydata \" " )
) ;
}
cryptonote : : blobdata blockblob ;
if ( ! epee : : string_tools : : parse_hexstr_to_binbuff ( request . params [ 0 ] . get_str ( ) , blockblob ) ) {
throw CN_JSONRPCError ( CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB , " Wrong block blob " ) ;
}
cryptonote : : block cnblock = AUTO_VAL_INIT ( cnblock ) ;
if ( ! cryptonote : : parse_and_validate_block_from_blob ( blockblob , cnblock ) ) {
throw CN_JSONRPCError ( CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB , " Wrong block blob " ) ;
}
cryptonote : : tx_extra_keva_block keva_block_blob ;
if ( ! cryptonote : : get_keva_block_from_extra ( cnblock . miner_tx . extra , keva_block_blob ) ) {
throw CN_JSONRPCError ( CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB , " Could not get Kevacoin block " ) ;
}
std : : shared_ptr < CBlock > blockptr = std : : make_shared < CBlock > ( ) ;
CBlock & block = * blockptr ;
const std : : vector < char > keva_block ( keva_block_blob . keva_block . begin ( ) , keva_block_blob . keva_block . end ( ) ) ;
CDataStream ssBlock ( keva_block , SER_NETWORK , PROTOCOL_VERSION ) ;
try {
ssBlock > > block ;
}
catch ( const std : : exception & ) {
throw CN_JSONRPCError ( CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB , " Failed to deserialize keva block " ) ;
}
if ( block . vtx . empty ( ) | | ! block . vtx [ 0 ] - > IsCoinBase ( ) ) {
throw CN_JSONRPCError ( CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB , " Block does not start with a coinbase " ) ;
}
block . cnHeader . major_version = cnblock . major_version ;
block . cnHeader . minor_version = cnblock . minor_version ;
block . cnHeader . timestamp = cnblock . timestamp ;
block . cnHeader . prev_id = CryptoHashToUint256 ( cnblock . prev_id ) ;
block . cnHeader . nonce = cnblock . nonce ;
crypto : : hash tree_root_hash = cryptonote : : get_tx_tree_hash ( cnblock ) ;
block . cnHeader . merkle_root = CryptoHashToUint256 ( tree_root_hash ) ;
block . cnHeader . nTxes = 1 ; // The Cryptonote coinbase tx.
uint256 hash = block . GetOriginalBlockHash ( ) ;
// Cryptonote prev_id is used to store the block hash of kevacoin.
if ( hash ! = block . cnHeader . prev_id ) {
throw CN_JSONRPCError ( CORE_RPC_ERROR_CODE_WRONG_PARAM , " Kevacoin block hash does not match cryptnote hash " ) ;
}
bool fBlockPresent = false ;
{
LOCK ( cs_main ) ;
BlockMap : : iterator mi = mapBlockIndex . find ( hash ) ;
if ( mi ! = mapBlockIndex . end ( ) ) {
CBlockIndex * pindex = mi - > second ;
if ( pindex - > IsValid ( BLOCK_VALID_SCRIPTS ) ) {
throw CN_JSONRPCError ( CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED , " duplicate " ) ;
}
if ( pindex - > nStatus & BLOCK_FAILED_MASK ) {
throw CN_JSONRPCError ( CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED , " duplicate-invalid " ) ;
}
// Otherwise, we might only have the header - process the block before returning
fBlockPresent = true ;
}
}
{
LOCK ( cs_main ) ;
BlockMap : : iterator mi = mapBlockIndex . find ( block . hashPrevBlock ) ;
if ( mi ! = mapBlockIndex . end ( ) ) {
UpdateUncommittedBlockStructures ( block , mi - > second , Params ( ) . GetConsensus ( ) ) ;
}
}
submitblock_StateCatcher sc ( block . GetHash ( ) ) ;
RegisterValidationInterface ( & sc ) ;
bool fAccepted = ProcessNewBlock ( Params ( ) , blockptr , true , nullptr ) ;
UnregisterValidationInterface ( & sc ) ;
if ( fBlockPresent ) {
if ( fAccepted & & ! sc . found ) {
throw CN_JSONRPCError ( CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED , " duplicate-inconclusive " ) ;
}
throw CN_JSONRPCError ( CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED , " duplicate " ) ;
}
if ( ! sc . found ) {
throw CN_JSONRPCError ( CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED , " inconclusive " ) ;
}
return BIP22ValidationResult_CN ( sc . state ) ;
}
UniValue estimatefee ( const JSONRPCRequest & request )
{
if ( request . fHelp | | request . params . size ( ) ! = 1 )
throw std : : runtime_error (
" estimatefee nblocks \n "
" \n DEPRECATED. Please use estimatesmartfee for more intelligent estimates. "
" \n Estimates the approximate fee per kilobyte needed for a transaction to begin \n "
" confirmation within nblocks blocks. Uses virtual transaction size of transaction \n "
" as defined in BIP 141 (witness data is discounted). \n "
" \n Arguments: \n "
" 1. nblocks (numeric, required) \n "
" \n Result: \n "
" n (numeric) estimated fee-per-kilobyte \n "
" \n "
" A negative value is returned if not enough transactions and blocks \n "
" have been observed to make an estimate. \n "
" -1 is always returned for nblocks == 1 as it is impossible to calculate \n "
" a fee that is high enough to get reliably included in the next block. \n "
" \n Example: \n "
+ HelpExampleCli ( " estimatefee " , " 6 " )
) ;
if ( ! IsDeprecatedRPCEnabled ( " estimatefee " ) ) {
throw JSONRPCError ( RPC_METHOD_DEPRECATED , " estimatefee is deprecated and will be fully removed in v0.17. "
" To use estimatefee in v0.16, restart kevacoind with -deprecatedrpc=estimatefee. \n "
" Projects should transition to using estimatesmartfee before upgrading to v0.17 " ) ;
}
RPCTypeCheck ( request . params , { UniValue : : VNUM } ) ;
int nBlocks = request . params [ 0 ] . get_int ( ) ;
if ( nBlocks < 1 )
nBlocks = 1 ;
CFeeRate feeRate = : : feeEstimator . estimateFee ( nBlocks ) ;
if ( feeRate = = CFeeRate ( 0 ) )
return - 1.0 ;
return ValueFromAmount ( feeRate . GetFeePerK ( ) ) ;
}
UniValue estimatesmartfee ( const JSONRPCRequest & request )
{
if ( request . fHelp | | request . params . size ( ) < 1 | | request . params . size ( ) > 2 )
throw std : : runtime_error (
" estimatesmartfee conf_target ( \" estimate_mode \" ) \n "
" \n Estimates the approximate fee per kilobyte needed for a transaction to begin \n "
" confirmation within conf_target blocks if possible and return the number of blocks \n "
" for which the estimate is valid. Uses virtual transaction size as defined \n "
" in BIP 141 (witness data is discounted). \n "
" \n Arguments: \n "
" 1. conf_target (numeric) Confirmation target in blocks (1 - 1008) \n "
" 2. \" estimate_mode \" (string, optional, default=CONSERVATIVE) The fee estimate mode. \n "
" Whether to return a more conservative estimate which also satisfies \n "
" a longer history. A conservative estimate potentially returns a \n "
" higher feerate and is more likely to be sufficient for the desired \n "
" target, but is not as responsive to short term drops in the \n "
" prevailing fee market. Must be one of: \n "
" \" UNSET \" (defaults to CONSERVATIVE) \n "
" \" ECONOMICAL \" \n "
" \" CONSERVATIVE \" \n "
" \n Result: \n "
" { \n "
" \" feerate \" : x.x, (numeric, optional) estimate fee rate in " + CURRENCY_UNIT + " /kB \n "
" \" errors \" : [ str... ] (json array of strings, optional) Errors encountered during processing \n "
" \" blocks \" : n (numeric) block number where estimate was found \n "
" } \n "
" \n "
" The request target will be clamped between 2 and the highest target \n "
" fee estimation is able to return based on how long it has been running. \n "
" An error is returned if not enough transactions and blocks \n "
" have been observed to make an estimate for any number of blocks. \n "
" \n Example: \n "
+ HelpExampleCli ( " estimatesmartfee " , " 6 " )
) ;
RPCTypeCheck ( request . params , { UniValue : : VNUM , UniValue : : VSTR } ) ;
RPCTypeCheckArgument ( request . params [ 0 ] , UniValue : : VNUM ) ;
unsigned int conf_target = ParseConfirmTarget ( request . params [ 0 ] ) ;
bool conservative = true ;
if ( ! request . params [ 1 ] . isNull ( ) ) {
FeeEstimateMode fee_mode ;
if ( ! FeeModeFromString ( request . params [ 1 ] . get_str ( ) , fee_mode ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Invalid estimate_mode parameter " ) ;
}
if ( fee_mode = = FeeEstimateMode : : ECONOMICAL ) conservative = false ;
}
UniValue result ( UniValue : : VOBJ ) ;
UniValue errors ( UniValue : : VARR ) ;
FeeCalculation feeCalc ;
CFeeRate feeRate = : : feeEstimator . estimateSmartFee ( conf_target , & feeCalc , conservative ) ;
if ( feeRate ! = CFeeRate ( 0 ) ) {
result . push_back ( Pair ( " feerate " , ValueFromAmount ( feeRate . GetFeePerK ( ) ) ) ) ;
} else {
errors . push_back ( " Insufficient data or no feerate found " ) ;
result . push_back ( Pair ( " errors " , errors ) ) ;
}
result . push_back ( Pair ( " blocks " , feeCalc . returnedTarget ) ) ;
return result ;
}
UniValue estimaterawfee ( const JSONRPCRequest & request )
{
if ( request . fHelp | | request . params . size ( ) < 1 | | request . params . size ( ) > 2 )
throw std : : runtime_error (
" estimaterawfee conf_target (threshold) \n "
" \n WARNING: This interface is unstable and may disappear or change! \n "
" \n WARNING: This is an advanced API call that is tightly coupled to the specific \n "
" implementation of fee estimation. The parameters it can be called with \n "
" and the results it returns will change if the internal implementation changes. \n "
" \n Estimates the approximate fee per kilobyte needed for a transaction to begin \n "
" confirmation within conf_target blocks if possible. Uses virtual transaction size as \n "
" defined in BIP 141 (witness data is discounted). \n "
" \n Arguments: \n "
" 1. conf_target (numeric) Confirmation target in blocks (1 - 1008) \n "
" 2. threshold (numeric, optional) The proportion of transactions in a given feerate range that must have been \n "
" confirmed within conf_target in order to consider those feerates as high enough and proceed to check \n "
" lower buckets. Default: 0.95 \n "
" \n Result: \n "
" { \n "
" \" short \" : { (json object, optional) estimate for short time horizon \n "
" \" feerate \" : x.x, (numeric, optional) estimate fee rate in " + CURRENCY_UNIT + " /kB \n "
" \" decay \" : x.x, (numeric) exponential decay (per block) for historical moving average of confirmation data \n "
" \" scale \" : x, (numeric) The resolution of confirmation targets at this time horizon \n "
" \" pass \" : { (json object, optional) information about the lowest range of feerates to succeed in meeting the threshold \n "
" \" startrange \" : x.x, (numeric) start of feerate range \n "
" \" endrange \" : x.x, (numeric) end of feerate range \n "
" \" withintarget \" : x.x, (numeric) number of txs over history horizon in the feerate range that were confirmed within target \n "
" \" totalconfirmed \" : x.x, (numeric) number of txs over history horizon in the feerate range that were confirmed at any point \n "
" \" inmempool \" : x.x, (numeric) current number of txs in mempool in the feerate range unconfirmed for at least target blocks \n "
" \" leftmempool \" : x.x, (numeric) number of txs over history horizon in the feerate range that left mempool unconfirmed after target \n "
" }, \n "
" \" fail \" : { ... }, (json object, optional) information about the highest range of feerates to fail to meet the threshold \n "
" \" errors \" : [ str... ] (json array of strings, optional) Errors encountered during processing \n "
" }, \n "
" \" medium \" : { ... }, (json object, optional) estimate for medium time horizon \n "
" \" long \" : { ... } (json object) estimate for long time horizon \n "
" } \n "
" \n "
" Results are returned for any horizon which tracks blocks up to the confirmation target. \n "
" \n Example: \n "
+ HelpExampleCli ( " estimaterawfee " , " 6 0.9 " )
) ;
RPCTypeCheck ( request . params , { UniValue : : VNUM , UniValue : : VNUM } , true ) ;
RPCTypeCheckArgument ( request . params [ 0 ] , UniValue : : VNUM ) ;
unsigned int conf_target = ParseConfirmTarget ( request . params [ 0 ] ) ;
double threshold = 0.95 ;
if ( ! request . params [ 1 ] . isNull ( ) ) {
threshold = request . params [ 1 ] . get_real ( ) ;
}
if ( threshold < 0 | | threshold > 1 ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Invalid threshold " ) ;
}
UniValue result ( UniValue : : VOBJ ) ;
for ( FeeEstimateHorizon horizon : { FeeEstimateHorizon : : SHORT_HALFLIFE , FeeEstimateHorizon : : MED_HALFLIFE , FeeEstimateHorizon : : LONG_HALFLIFE } ) {
CFeeRate feeRate ;
EstimationResult buckets ;
// Only output results for horizons which track the target
if ( conf_target > : : feeEstimator . HighestTargetTracked ( horizon ) ) continue ;
feeRate = : : feeEstimator . estimateRawFee ( conf_target , threshold , horizon , & buckets ) ;
UniValue horizon_result ( UniValue : : VOBJ ) ;
UniValue errors ( UniValue : : VARR ) ;
UniValue passbucket ( UniValue : : VOBJ ) ;
passbucket . push_back ( Pair ( " startrange " , round ( buckets . pass . start ) ) ) ;
passbucket . push_back ( Pair ( " endrange " , round ( buckets . pass . end ) ) ) ;
passbucket . push_back ( Pair ( " withintarget " , round ( buckets . pass . withinTarget * 100.0 ) / 100.0 ) ) ;
passbucket . push_back ( Pair ( " totalconfirmed " , round ( buckets . pass . totalConfirmed * 100.0 ) / 100.0 ) ) ;
passbucket . push_back ( Pair ( " inmempool " , round ( buckets . pass . inMempool * 100.0 ) / 100.0 ) ) ;
passbucket . push_back ( Pair ( " leftmempool " , round ( buckets . pass . leftMempool * 100.0 ) / 100.0 ) ) ;
UniValue failbucket ( UniValue : : VOBJ ) ;
failbucket . push_back ( Pair ( " startrange " , round ( buckets . fail . start ) ) ) ;
failbucket . push_back ( Pair ( " endrange " , round ( buckets . fail . end ) ) ) ;
failbucket . push_back ( Pair ( " withintarget " , round ( buckets . fail . withinTarget * 100.0 ) / 100.0 ) ) ;
failbucket . push_back ( Pair ( " totalconfirmed " , round ( buckets . fail . totalConfirmed * 100.0 ) / 100.0 ) ) ;
failbucket . push_back ( Pair ( " inmempool " , round ( buckets . fail . inMempool * 100.0 ) / 100.0 ) ) ;
failbucket . push_back ( Pair ( " leftmempool " , round ( buckets . fail . leftMempool * 100.0 ) / 100.0 ) ) ;
// CFeeRate(0) is used to indicate error as a return value from estimateRawFee
if ( feeRate ! = CFeeRate ( 0 ) ) {
horizon_result . push_back ( Pair ( " feerate " , ValueFromAmount ( feeRate . GetFeePerK ( ) ) ) ) ;
horizon_result . push_back ( Pair ( " decay " , buckets . decay ) ) ;
horizon_result . push_back ( Pair ( " scale " , ( int ) buckets . scale ) ) ;
horizon_result . push_back ( Pair ( " pass " , passbucket ) ) ;
// buckets.fail.start == -1 indicates that all buckets passed, there is no fail bucket to output
if ( buckets . fail . start ! = - 1 ) horizon_result . push_back ( Pair ( " fail " , failbucket ) ) ;
} else {
// Output only information that is still meaningful in the event of error
horizon_result . push_back ( Pair ( " decay " , buckets . decay ) ) ;
horizon_result . push_back ( Pair ( " scale " , ( int ) buckets . scale ) ) ;
horizon_result . push_back ( Pair ( " fail " , failbucket ) ) ;
errors . push_back ( " Insufficient data or no feerate found which meets threshold " ) ;
horizon_result . push_back ( Pair ( " errors " , errors ) ) ;
}
result . push_back ( Pair ( StringForFeeEstimateHorizon ( horizon ) , horizon_result ) ) ;
}
return result ;
}
static const CRPCCommand commands [ ] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
{ " mining " , " getnetworkhashps " , & getnetworkhashps , { " nblocks " , " height " } } ,
{ " mining " , " getmininginfo " , & getmininginfo , { } } ,
{ " mining " , " prioritisetransaction " , & prioritisetransaction , { " txid " , " dummy " , " fee_delta " } } ,
{ " mining " , " getblocktemplate " , & getblocktemplate , { " reserve_size " , " wallet_address " } } ,
{ " mining " , " getblocktemplate_original " , & getblocktemplate_original , { " template_request " } } ,
{ " mining " , " submitblock " , & submitblock , { " hexdata " , " dummy " } } ,
{ " mining " , " submitblock_original " , & submitblock_original , { " hexdata " , " dummy " } } ,
{ " generating " , " generatetoaddress " , & generatetoaddress , { " nblocks " , " address " , " maxtries " } } ,
{ " util " , " estimatefee " , & estimatefee , { " nblocks " } } ,
{ " util " , " estimatesmartfee " , & estimatesmartfee , { " conf_target " , " estimate_mode " } } ,
{ " hidden " , " estimaterawfee " , & estimaterawfee , { " conf_target " , " threshold " } } ,
} ;
void RegisterMiningRPCCommands ( CRPCTable & t )
{
for ( unsigned int vcidx = 0 ; vcidx < ARRAYLEN ( commands ) ; vcidx + + )
t . appendCommand ( commands [ vcidx ] . name , & commands [ vcidx ] ) ;
}