27e507cba0
This adds a unit test to see if the combination of invalidateBlock and reconsiderBlock do what we expect to do. The main issue was that we store an invalidated block in the UTXO and we forgot to re-validate that on reconsider. Additionally I avoid writing a lot of unchanged data to the block-index.
833 lines
35 KiB
C++
833 lines
35 KiB
C++
/*
|
|
* This file is part of the Flowee project
|
|
* Copyright (C) 2010 Satoshi Nakamoto
|
|
* Copyright (C) 2009-2015 The Bitcoin Core developers
|
|
* Copyright (C) 2018-2020 Tom Zander <tomz@freedommail.ch>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
#include "amount.h"
|
|
#include "SettingsDefaults.h"
|
|
#include "chain.h"
|
|
#include "chainparams.h"
|
|
#include "checkpoints.h"
|
|
#include "consensus/validation.h"
|
|
#include "main.h"
|
|
#include "policy/policy.h"
|
|
#include "primitives/transaction.h"
|
|
#include "primitives/FastBlock.h"
|
|
#include "rpcserver.h"
|
|
#include "streaming/streams.h"
|
|
#include "sync.h"
|
|
#include "txmempool.h"
|
|
#include "BlocksDB.h"
|
|
#include "timedata.h"
|
|
#include "util.h"
|
|
#include "utilstrencodings.h"
|
|
#include "UnspentOutputData.h"
|
|
#include "primitives/block.h"
|
|
#include <Application.h>
|
|
#include <validation/Engine.h>
|
|
#include <validation/VerifyDB.h>
|
|
|
|
#include <boost/foreach.hpp>
|
|
|
|
extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
|
|
void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
|
|
|
|
double GetDifficulty(const CBlockIndex* blockindex)
|
|
{
|
|
// Floating point number that is a multiple of the minimum difficulty,
|
|
// minimum difficulty = 1.0.
|
|
if (blockindex == NULL)
|
|
{
|
|
if (chainActive.Tip() == NULL)
|
|
return 1.0;
|
|
else
|
|
blockindex = chainActive.Tip();
|
|
}
|
|
|
|
int nShift = (blockindex->nBits >> 24) & 0xff;
|
|
|
|
double dDiff =
|
|
(double)0x0000ffff / (double)(blockindex->nBits & 0x00ffffff);
|
|
|
|
while (nShift < 29)
|
|
{
|
|
dDiff *= 256.0;
|
|
nShift++;
|
|
}
|
|
while (nShift > 29)
|
|
{
|
|
dDiff /= 256.0;
|
|
nShift--;
|
|
}
|
|
|
|
return dDiff;
|
|
}
|
|
|
|
UniValue blockheaderToJSON(const CBlockIndex* blockindex)
|
|
{
|
|
UniValue result(UniValue::VOBJ);
|
|
result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex()));
|
|
int confirmations = -1;
|
|
// Only report confirmations if the block is on the main chain
|
|
if (chainActive.Contains(blockindex))
|
|
confirmations = chainActive.Height() - blockindex->nHeight + 1;
|
|
result.push_back(Pair("confirmations", confirmations));
|
|
result.push_back(Pair("height", blockindex->nHeight));
|
|
result.push_back(Pair("version", blockindex->nVersion));
|
|
result.push_back(Pair("merkleroot", blockindex->hashMerkleRoot.GetHex()));
|
|
result.push_back(Pair("time", (int64_t)blockindex->nTime));
|
|
result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast()));
|
|
result.push_back(Pair("nonce", (uint64_t)blockindex->nNonce));
|
|
result.push_back(Pair("bits", strprintf("%08x", blockindex->nBits)));
|
|
result.push_back(Pair("difficulty", GetDifficulty(blockindex)));
|
|
result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex()));
|
|
|
|
if (blockindex->pprev)
|
|
result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()));
|
|
CBlockIndex *pnext = chainActive.Next(blockindex);
|
|
if (pnext)
|
|
result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex()));
|
|
return result;
|
|
}
|
|
|
|
UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false)
|
|
{
|
|
UniValue result(UniValue::VOBJ);
|
|
result.push_back(Pair("hash", block.GetHash().GetHex()));
|
|
int confirmations = -1;
|
|
// Only report confirmations if the block is on the main chain
|
|
if (chainActive.Contains(blockindex))
|
|
confirmations = chainActive.Height() - blockindex->nHeight + 1;
|
|
result.push_back(Pair("confirmations", confirmations));
|
|
result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION)));
|
|
result.push_back(Pair("height", blockindex->nHeight));
|
|
result.push_back(Pair("version", block.nVersion));
|
|
result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex()));
|
|
UniValue txs(UniValue::VARR);
|
|
BOOST_FOREACH(const CTransaction&tx, block.vtx)
|
|
{
|
|
if(txDetails)
|
|
{
|
|
UniValue objTx(UniValue::VOBJ);
|
|
TxToJSON(tx, uint256(), objTx);
|
|
txs.push_back(objTx);
|
|
}
|
|
else
|
|
txs.push_back(tx.GetHash().GetHex());
|
|
}
|
|
result.push_back(Pair("tx", txs));
|
|
result.push_back(Pair("time", block.GetBlockTime()));
|
|
result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast()));
|
|
result.push_back(Pair("nonce", (uint64_t)block.nNonce));
|
|
result.push_back(Pair("bits", strprintf("%08x", block.nBits)));
|
|
result.push_back(Pair("difficulty", GetDifficulty(blockindex)));
|
|
result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex()));
|
|
|
|
if (blockindex->pprev)
|
|
result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()));
|
|
CBlockIndex *pnext = chainActive.Next(blockindex);
|
|
if (pnext)
|
|
result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex()));
|
|
return result;
|
|
}
|
|
|
|
UniValue getblockcount(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() != 0)
|
|
throw std::runtime_error(
|
|
"getblockcount\n"
|
|
"\nReturns the number of blocks in the longest block chain.\n"
|
|
"\nResult:\n"
|
|
"n (numeric) The current block count\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("getblockcount", "")
|
|
+ HelpExampleRpc("getblockcount", "")
|
|
);
|
|
|
|
LOCK(cs_main);
|
|
return chainActive.Height();
|
|
}
|
|
|
|
UniValue getbestblockhash(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() != 0)
|
|
throw std::runtime_error(
|
|
"getbestblockhash\n"
|
|
"\nReturns the hash of the best (tip) block in the longest block chain.\n"
|
|
"\nResult\n"
|
|
"\"hex\" (string) the block hash hex encoded\n"
|
|
"\nExamples\n"
|
|
+ HelpExampleCli("getbestblockhash", "")
|
|
+ HelpExampleRpc("getbestblockhash", "")
|
|
);
|
|
|
|
LOCK(cs_main);
|
|
return chainActive.Tip()->GetBlockHash().GetHex();
|
|
}
|
|
|
|
UniValue getdifficulty(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() != 0)
|
|
throw std::runtime_error(
|
|
"getdifficulty\n"
|
|
"\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n"
|
|
"\nResult:\n"
|
|
"n.nnn (numeric) the proof-of-work difficulty as a multiple of the minimum difficulty.\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("getdifficulty", "")
|
|
+ HelpExampleRpc("getdifficulty", "")
|
|
);
|
|
|
|
LOCK(cs_main);
|
|
return GetDifficulty();
|
|
}
|
|
|
|
UniValue mempoolToJSON(bool fVerbose = false)
|
|
{
|
|
if (fVerbose)
|
|
{
|
|
LOCK(mempool.cs);
|
|
UniValue o(UniValue::VOBJ);
|
|
BOOST_FOREACH(const CTxMemPoolEntry& e, mempool.mapTx)
|
|
{
|
|
const uint256& hash = e.GetTx().GetHash();
|
|
UniValue info(UniValue::VOBJ);
|
|
info.push_back(Pair("size", (int)e.GetTxSize()));
|
|
info.push_back(Pair("fee", ValueFromAmount(e.GetFee())));
|
|
info.push_back(Pair("modifiedfee", ValueFromAmount(e.GetModifiedFee())));
|
|
info.push_back(Pair("time", e.GetTime()));
|
|
info.push_back(Pair("height", (int)e.GetHeight()));
|
|
info.push_back(Pair("startingpriority", e.GetPriority(e.GetHeight())));
|
|
info.push_back(Pair("currentpriority", e.GetPriority(chainActive.Height())));
|
|
info.push_back(Pair("descendantcount", e.GetCountWithDescendants()));
|
|
info.push_back(Pair("descendantsize", e.GetSizeWithDescendants()));
|
|
info.push_back(Pair("descendantfees", e.GetModFeesWithDescendants()));
|
|
const CTransaction& tx = e.GetTx();
|
|
std::set<std::string> setDepends;
|
|
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
|
{
|
|
if (mempool.exists(txin.prevout.hash))
|
|
setDepends.insert(txin.prevout.hash.ToString());
|
|
}
|
|
|
|
UniValue depends(UniValue::VARR);
|
|
BOOST_FOREACH(const std::string& dep, setDepends)
|
|
{
|
|
depends.push_back(dep);
|
|
}
|
|
|
|
info.push_back(Pair("depends", depends));
|
|
o.push_back(Pair(hash.ToString(), info));
|
|
}
|
|
return o;
|
|
}
|
|
else
|
|
{
|
|
std::vector<uint256> vtxid;
|
|
mempool.queryHashes(vtxid);
|
|
|
|
UniValue a(UniValue::VARR);
|
|
BOOST_FOREACH(const uint256& hash, vtxid)
|
|
a.push_back(hash.ToString());
|
|
|
|
return a;
|
|
}
|
|
}
|
|
|
|
UniValue getrawmempool(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() > 1)
|
|
throw std::runtime_error(
|
|
"getrawmempool ( verbose )\n"
|
|
"\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n"
|
|
"\nArguments:\n"
|
|
"1. verbose (boolean, optional, default=false) true for a json object, false for array of transaction ids\n"
|
|
"\nResult: (for verbose = false):\n"
|
|
"[ (json array of string)\n"
|
|
" \"transactionid\" (string) The transaction id\n"
|
|
" ,...\n"
|
|
"]\n"
|
|
"\nResult: (for verbose = true):\n"
|
|
"{ (json object)\n"
|
|
" \"transactionid\" : { (json object)\n"
|
|
" \"size\" : n, (numeric) transaction size in bytes\n"
|
|
" \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n"
|
|
" \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority\n"
|
|
" \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n"
|
|
" \"height\" : n, (numeric) block height when transaction entered pool\n"
|
|
" \"startingpriority\" : n, (numeric) priority when transaction entered pool\n"
|
|
" \"currentpriority\" : n, (numeric) transaction priority now\n"
|
|
" \"descendantcount\" : n, (numeric) number of in-mempool descendant transactions (including this one)\n"
|
|
" \"descendantsize\" : n, (numeric) size of in-mempool descendants (including this one)\n"
|
|
" \"descendantfees\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one)\n"
|
|
" \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n"
|
|
" \"transactionid\", (string) parent transaction id\n"
|
|
" ... ]\n"
|
|
" }, ...\n"
|
|
"}\n"
|
|
"\nExamples\n"
|
|
+ HelpExampleCli("getrawmempool", "true")
|
|
+ HelpExampleRpc("getrawmempool", "true")
|
|
);
|
|
|
|
LOCK(cs_main);
|
|
|
|
bool fVerbose = false;
|
|
if (params.size() > 0)
|
|
fVerbose = params[0].get_bool();
|
|
|
|
return mempoolToJSON(fVerbose);
|
|
}
|
|
|
|
UniValue getblockhash(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() != 1)
|
|
throw std::runtime_error(
|
|
"getblockhash index\n"
|
|
"\nReturns hash of block in best-block-chain at index provided.\n"
|
|
"\nArguments:\n"
|
|
"1. index (numeric, required) The block index\n"
|
|
"\nResult:\n"
|
|
"\"hash\" (string) The block hash\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("getblockhash", "1000")
|
|
+ HelpExampleRpc("getblockhash", "1000")
|
|
);
|
|
|
|
LOCK(cs_main);
|
|
|
|
int nHeight = params[0].get_int();
|
|
if (nHeight < 0 || nHeight > chainActive.Height())
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
|
|
|
|
CBlockIndex* pblockindex = chainActive[nHeight];
|
|
return pblockindex->GetBlockHash().GetHex();
|
|
}
|
|
|
|
UniValue getblockheader(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() < 1 || params.size() > 2)
|
|
throw std::runtime_error(
|
|
"getblockheader \"hash\" ( verbose )\n"
|
|
"\nIf verbose is false, returns a string that is serialized, hex-encoded data for blockheader 'hash'.\n"
|
|
"If verbose is true, returns an Object with information about blockheader <hash>.\n"
|
|
"\nArguments:\n"
|
|
"1. \"hash\" (string, required) The block hash\n"
|
|
"2. verbose (boolean, optional, default=true) true for a json object, false for the hex encoded data\n"
|
|
"\nResult (for verbose = true):\n"
|
|
"{\n"
|
|
" \"hash\" : \"hash\", (string) the block hash (same as provided)\n"
|
|
" \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n"
|
|
" \"height\" : n, (numeric) The block height or index\n"
|
|
" \"version\" : n, (numeric) The block version\n"
|
|
" \"merkleroot\" : \"xxxx\", (string) The merkle root\n"
|
|
" \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n"
|
|
" \"mediantime\" : ttt, (numeric) The median block time in seconds since epoch (Jan 1 1970 GMT)\n"
|
|
" \"nonce\" : n, (numeric) The nonce\n"
|
|
" \"bits\" : \"1d00ffff\", (string) The bits\n"
|
|
" \"difficulty\" : x.xxx, (numeric) The difficulty\n"
|
|
" \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n"
|
|
" \"nextblockhash\" : \"hash\", (string) The hash of the next block\n"
|
|
" \"chainwork\" : \"0000...1f3\" (string) Expected number of hashes required to produce the current chain (in hex)\n"
|
|
"}\n"
|
|
"\nResult (for verbose=false):\n"
|
|
"\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
|
|
+ HelpExampleRpc("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
|
|
);
|
|
|
|
LOCK(cs_main);
|
|
|
|
std::string strHash = params[0].get_str();
|
|
uint256 hash(uint256S(strHash));
|
|
|
|
bool fVerbose = true;
|
|
if (params.size() > 1)
|
|
fVerbose = params[1].get_bool();
|
|
|
|
CBlockIndex* pblockindex = Blocks::Index::get(hash);
|
|
if (!pblockindex)
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
|
|
|
|
if (!fVerbose)
|
|
{
|
|
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
|
|
ssBlock << pblockindex->GetBlockHeader();
|
|
std::string strHex = HexStr(ssBlock.begin(), ssBlock.end());
|
|
return strHex;
|
|
}
|
|
|
|
return blockheaderToJSON(pblockindex);
|
|
}
|
|
|
|
UniValue getblock(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() < 1 || params.size() > 2)
|
|
throw std::runtime_error(
|
|
"getblock \"hash\" ( verbose )\n"
|
|
"\nIf verbose is false, returns a string that is serialized, hex-encoded data for block 'hash'.\n"
|
|
"If verbose is true, returns an Object with information about block <hash>.\n"
|
|
"\nArguments:\n"
|
|
"1. \"hash\" (string, required) The block hash\n"
|
|
"2. verbose (boolean, optional, default=true) true for a json object, false for the hex encoded data\n"
|
|
"\nResult (for verbose = true):\n"
|
|
"{\n"
|
|
" \"hash\" : \"hash\", (string) the block hash (same as provided)\n"
|
|
" \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n"
|
|
" \"size\" : n, (numeric) The block size\n"
|
|
" \"height\" : n, (numeric) The block height or index\n"
|
|
" \"version\" : n, (numeric) The block version\n"
|
|
" \"merkleroot\" : \"xxxx\", (string) The merkle root\n"
|
|
" \"tx\" : [ (array of string) The transaction ids\n"
|
|
" \"transactionid\" (string) The transaction id\n"
|
|
" ,...\n"
|
|
" ],\n"
|
|
" \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n"
|
|
" \"mediantime\" : ttt, (numeric) The median block time in seconds since epoch (Jan 1 1970 GMT)\n"
|
|
" \"nonce\" : n, (numeric) The nonce\n"
|
|
" \"bits\" : \"1d00ffff\", (string) The bits\n"
|
|
" \"difficulty\" : x.xxx, (numeric) The difficulty\n"
|
|
" \"chainwork\" : \"xxxx\", (string) Expected number of hashes required to produce the chain up to this block (in hex)\n"
|
|
" \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n"
|
|
" \"nextblockhash\" : \"hash\" (string) The hash of the next block\n"
|
|
"}\n"
|
|
"\nResult (for verbose=false):\n"
|
|
"\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
|
|
+ HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
|
|
);
|
|
|
|
LOCK(cs_main);
|
|
|
|
std::string strHash = params[0].get_str();
|
|
uint256 hash(uint256S(strHash));
|
|
|
|
bool fVerbose = true;
|
|
if (params.size() > 1)
|
|
fVerbose = params[1].get_bool();
|
|
|
|
CBlockIndex* pblockindex = Blocks::Index::get(hash);
|
|
if (!pblockindex)
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
|
|
|
|
CBlock block;
|
|
if(!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus()))
|
|
throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
|
|
|
|
if (!fVerbose)
|
|
{
|
|
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
|
|
ssBlock << block;
|
|
std::string strHex = HexStr(ssBlock.begin(), ssBlock.end());
|
|
return strHex;
|
|
}
|
|
|
|
return blockToJSON(block, pblockindex);
|
|
}
|
|
|
|
UniValue gettxout(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() < 2 || params.size() > 3)
|
|
throw std::runtime_error(
|
|
"gettxout \"txid\" n ( includemempool )\n"
|
|
"\nReturns details about an unspent transaction output.\n"
|
|
"\nArguments:\n"
|
|
"1. \"txid\" (string, required) The transaction id\n"
|
|
"2. n (numeric, required) vout value\n"
|
|
"3. includemempool (boolean, optional) Whether to included the mem pool\n"
|
|
"\nResult:\n"
|
|
"{\n"
|
|
" \"bestblock\" : \"hash\", (string) the block hash\n"
|
|
" \"confirmations\" : n, (numeric) The number of confirmations\n"
|
|
" \"value\" : x.xxx, (numeric) The transaction value in " + CURRENCY_UNIT + "\n"
|
|
" \"scriptPubKey\" : { (json object)\n"
|
|
" \"asm\" : \"code\", (string) \n"
|
|
" \"hex\" : \"hex\", (string) \n"
|
|
" \"reqSigs\" : n, (numeric) Number of required signatures\n"
|
|
" \"type\" : \"pubkeyhash\", (string) The type, eg pubkeyhash\n"
|
|
" \"addresses\" : [ (array of string) array of bitcoin addresses\n"
|
|
" \"bitcoinaddress\" (string) bitcoin address\n"
|
|
" ,...\n"
|
|
" ]\n"
|
|
" },\n"
|
|
" \"version\" : n, (numeric) The version\n"
|
|
" \"coinbase\" : true|false (boolean) Coinbase or not\n"
|
|
"}\n"
|
|
|
|
"\nExamples:\n"
|
|
"\nGet unspent transactions\n"
|
|
+ HelpExampleCli("listunspent", "") +
|
|
"\nView the details\n"
|
|
+ HelpExampleCli("gettxout", "\"txid\" 1") +
|
|
"\nAs a json rpc call\n"
|
|
+ HelpExampleRpc("gettxout", "\"txid\", 1")
|
|
);
|
|
|
|
LOCK(cs_main);
|
|
|
|
UniValue ret(UniValue::VOBJ);
|
|
|
|
std::string strHash = params[0].get_str();
|
|
uint256 hash(uint256S(strHash));
|
|
int n = params[1].get_int();
|
|
bool fMempool = true;
|
|
if (params.size() > 2)
|
|
fMempool = params[2].get_bool();
|
|
|
|
int confirmedHeight;
|
|
std::uint64_t value = -1;
|
|
CScript outScript;
|
|
int version;
|
|
bool isCoinbase;
|
|
bool found = false;
|
|
if (fMempool) {
|
|
LOCK(mempool.cs);
|
|
CTransaction tx;
|
|
if (mempool.lookup(hash, tx) && tx.vout.size() > n) { // found it!
|
|
confirmedHeight = MEMPOOL_HEIGHT;
|
|
value = tx.vout[n].nValue;
|
|
outScript = tx.vout[n].scriptPubKey;
|
|
version = tx.nVersion;
|
|
isCoinbase = n == 0;
|
|
found = true;
|
|
}
|
|
}
|
|
if (!found) {
|
|
auto result = g_utxo->find(hash, n);
|
|
if (result.isValid()) {
|
|
UnspentOutputData data(result);
|
|
confirmedHeight = data.blockHeight();
|
|
value = data.outputValue();
|
|
outScript = data.outputScript();
|
|
version = data.prevTxVersion();
|
|
isCoinbase = data.isCoinbase();
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
return NullUniValue;
|
|
CBlockIndex *pindex = Blocks::Index::get(g_utxo->blockId());
|
|
assert(pindex);
|
|
ret.push_back(Pair("bestblock", pindex->GetBlockHash().GetHex()));
|
|
if ((unsigned int)confirmedHeight == MEMPOOL_HEIGHT)
|
|
ret.push_back(Pair("confirmations", 0));
|
|
else
|
|
ret.push_back(Pair("confirmations", pindex->nHeight - confirmedHeight + 1));
|
|
ret.push_back(Pair("value", ValueFromAmount(value)));
|
|
UniValue o(UniValue::VOBJ);
|
|
ScriptPubKeyToJSON(outScript, o, true);
|
|
ret.push_back(Pair("scriptPubKey", o));
|
|
ret.push_back(Pair("version", version));
|
|
ret.push_back(Pair("coinbase", isCoinbase));
|
|
|
|
return ret;
|
|
}
|
|
|
|
UniValue verifychain(const UniValue& params, bool fHelp)
|
|
{
|
|
int nCheckLevel = GetArg("-checklevel", Settings::DefaultCheckLevel);
|
|
int nCheckDepth = GetArg("-checkblocks", Settings::DefaultCheckBlocks);
|
|
if (fHelp || params.size() > 2)
|
|
throw std::runtime_error(
|
|
"verifychain ( checklevel numblocks )\n"
|
|
"\nVerifies blockchain database.\n"
|
|
"\nArguments:\n"
|
|
"1. checklevel (numeric, optional, 0-4, default=" + strprintf("%d", nCheckLevel) + ") How thorough the block verification is.\n"
|
|
"2. numblocks (numeric, optional, default=" + strprintf("%d", nCheckDepth) + ", 0=all) The number of blocks to check.\n"
|
|
"\nResult:\n"
|
|
"true|false (boolean) Verified or not\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("verifychain", "")
|
|
+ HelpExampleRpc("verifychain", "")
|
|
);
|
|
|
|
if (params.size() > 0)
|
|
nCheckLevel = params[0].get_int();
|
|
if (params.size() > 1)
|
|
nCheckDepth = params[1].get_int();
|
|
|
|
return VerifyDB().verifyDB(nCheckLevel, nCheckDepth);
|
|
}
|
|
|
|
UniValue getblockchaininfo(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() != 0)
|
|
throw std::runtime_error(
|
|
"getblockchaininfo\n"
|
|
"Returns an object containing various state info regarding block chain processing.\n"
|
|
"\nResult:\n"
|
|
"{\n"
|
|
" \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n"
|
|
" \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n"
|
|
" \"headers\": xxxxxx, (numeric) the current number of headers we have validated\n"
|
|
" \"bestblockhash\": \"...\", (string) the hash of the currently best block\n"
|
|
" \"difficulty\": xxxxxx, (numeric) the current difficulty\n"
|
|
" \"mediantime\": xxxxxx, (numeric) median time for the current best block\n"
|
|
" \"verificationprogress\": xxxx, (numeric) estimate of verification progress [0..1]\n"
|
|
" \"chainwork\": \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n"
|
|
" \"pruned\": xx, (boolean) if the blocks are subject to pruning\n"
|
|
" \"pruneheight\": xxxxxx, (numeric) heighest block available\n"
|
|
" \"softforks\": [ (array) status of softforks in progress\n"
|
|
" {\n"
|
|
" \"id\": \"xxxx\", (string) name of softfork\n"
|
|
" \"version\": xx, (numeric) block version\n"
|
|
" \"enforce\": { (object) progress toward enforcing the softfork rules for new-version blocks\n"
|
|
" \"status\": xx, (boolean) true if threshold reached\n"
|
|
" \"found\": xx, (numeric) number of blocks with the new version found\n"
|
|
" \"required\": xx, (numeric) number of blocks required to trigger\n"
|
|
" \"window\": xx, (numeric) maximum size of examined window of recent blocks\n"
|
|
" },\n"
|
|
" \"reject\": { ... } (object) progress toward rejecting pre-softfork blocks (same fields as \"enforce\")\n"
|
|
" }, ...\n"
|
|
" ],\n"
|
|
" \"bip9_softforks\": [ (array) status of BIP9 softforks in progress\n"
|
|
" {\n"
|
|
" \"id\": \"xxxx\", (string) name of the softfork\n"
|
|
" \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"lockedin\", \"active\", \"failed\"\n"
|
|
" }\n"
|
|
" ]\n"
|
|
"}\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("getblockchaininfo", "")
|
|
+ HelpExampleRpc("getblockchaininfo", "")
|
|
);
|
|
|
|
LOCK(cs_main);
|
|
|
|
UniValue obj(UniValue::VOBJ);
|
|
obj.push_back(Pair("chain", Params().NetworkIDString()));
|
|
obj.push_back(Pair("blocks", (int)chainActive.Height()));
|
|
obj.push_back(Pair("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1));
|
|
obj.push_back(Pair("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex()));
|
|
obj.push_back(Pair("difficulty", (double)GetDifficulty()));
|
|
obj.push_back(Pair("mediantime", (int64_t)chainActive.Tip()->GetMedianTimePast()));
|
|
obj.push_back(Pair("verificationprogress", Checkpoints::GuessVerificationProgress(Params().Checkpoints(), chainActive.Tip())));
|
|
obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex()));
|
|
obj.push_back(Pair("initialblockdownload",
|
|
(Blocks::DB::instance()->headerChain().Height() - chainActive.Height() > 1000)));
|
|
obj.push_back(Pair("pruned", false));
|
|
return obj;
|
|
}
|
|
|
|
/** Comparison function for sorting the getchaintips heads. */
|
|
struct CompareBlocksByHeight
|
|
{
|
|
bool operator()(const CBlockIndex* a, const CBlockIndex* b) const
|
|
{
|
|
/* Make sure that unequal blocks with the same height do not compare
|
|
equal. Use the pointers themselves to make a distinction. */
|
|
|
|
if (a->nHeight != b->nHeight)
|
|
return (a->nHeight > b->nHeight);
|
|
|
|
return a < b;
|
|
}
|
|
};
|
|
|
|
UniValue getchaintips(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() != 0)
|
|
throw std::runtime_error(
|
|
"getchaintips\n"
|
|
"Return information about all known tips in the block tree,"
|
|
" including the main chain as well as orphaned branches.\n"
|
|
"\nResult:\n"
|
|
"[\n"
|
|
" {\n"
|
|
" \"height\": xxxx, (numeric) height of the chain tip\n"
|
|
" \"hash\": \"xxxx\", (string) block hash of the tip\n"
|
|
" \"branchlen\": 0 (numeric) zero for main chain\n"
|
|
" \"status\": \"active\" (string) \"active\" for the main chain\n"
|
|
" },\n"
|
|
" {\n"
|
|
" \"height\": xxxx,\n"
|
|
" \"hash\": \"xxxx\",\n"
|
|
" \"branchlen\": 1 (numeric) length of branch connecting the tip to the main chain\n"
|
|
" \"status\": \"xxxx\" (string) status of the chain (active, valid-fork, valid-headers, headers-only, invalid)\n"
|
|
" }\n"
|
|
"]\n"
|
|
"Possible values for status:\n"
|
|
"1. \"invalid\" This branch contains at least one invalid block\n"
|
|
"2. \"headers-only\" Not all blocks for this branch are available, but the headers are valid\n"
|
|
"3. \"valid-headers\" All blocks are available for this branch, but they were never fully validated\n"
|
|
"4. \"valid-fork\" This branch is not part of the active chain, but is fully validated\n"
|
|
"5. \"active\" This is the tip of the active main chain, which is certainly valid\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("getchaintips", "")
|
|
+ HelpExampleRpc("getchaintips", "")
|
|
);
|
|
|
|
LOCK(cs_main);
|
|
|
|
/* Build up a list of chain tips. We start with the list of all
|
|
known blocks, and successively remove blocks that appear as pprev
|
|
of another block. */
|
|
std::set<const CBlockIndex*, CompareBlocksByHeight> setTips;
|
|
for (auto index : Blocks::DB::instance()->headerChainTips()) {
|
|
setTips.insert(index);
|
|
}
|
|
|
|
/* Construct the output array. */
|
|
UniValue res(UniValue::VARR);
|
|
BOOST_FOREACH(const CBlockIndex* block, setTips)
|
|
{
|
|
UniValue obj(UniValue::VOBJ);
|
|
obj.push_back(Pair("height", block->nHeight));
|
|
obj.push_back(Pair("hash", block->phashBlock->GetHex()));
|
|
|
|
const int branchLen = block->nHeight - chainActive.FindFork(block)->nHeight;
|
|
obj.push_back(Pair("branchlen", branchLen));
|
|
|
|
std::string status;
|
|
if (chainActive.Contains(block)) {
|
|
// This block is part of the currently active chain.
|
|
status = "active";
|
|
} else if (block->nStatus & BLOCK_FAILED_MASK) {
|
|
// This block or one of its ancestors is invalid.
|
|
status = "invalid";
|
|
} else if (block->nChainTx == 0) {
|
|
// This block cannot be connected because full block data for it or one of its parents is missing.
|
|
status = "headers-only";
|
|
} else if (block->IsValid(BLOCK_VALID_SCRIPTS)) {
|
|
// This block is fully validated, but no longer part of the active chain. It was probably the active block once, but was reorganized.
|
|
status = "valid-fork";
|
|
} else if (block->IsValid(BLOCK_VALID_TREE)) {
|
|
// The headers for this block are valid, but it has not been validated. It was probably never part of the most-work chain.
|
|
status = "valid-headers";
|
|
} else {
|
|
// No clue.
|
|
status = "unknown";
|
|
}
|
|
obj.push_back(Pair("status", status));
|
|
|
|
res.push_back(obj);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
UniValue mempoolInfoToJSON()
|
|
{
|
|
UniValue ret(UniValue::VOBJ);
|
|
ret.push_back(Pair("size", (int64_t) mempool.size()));
|
|
ret.push_back(Pair("bytes", (int64_t) mempool.GetTotalTxSize()));
|
|
ret.push_back(Pair("usage", (int64_t) mempool.DynamicMemoryUsage()));
|
|
size_t maxmempool = GetArg("-maxmempool", Settings::DefaultMaxMempoolSize) * 1000000;
|
|
ret.push_back(Pair("maxmempool", (int64_t) maxmempool));
|
|
ret.push_back(Pair("mempoolminfee", ValueFromAmount(mempool.GetMinFee().GetFeePerK())));
|
|
|
|
return ret;
|
|
}
|
|
|
|
UniValue getmempoolinfo(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() != 0)
|
|
throw std::runtime_error(
|
|
"getmempoolinfo\n"
|
|
"\nReturns details on the active state of the TX memory pool.\n"
|
|
"\nResult:\n"
|
|
"{\n"
|
|
" \"size\": xxxxx, (numeric) Current tx count\n"
|
|
" \"bytes\": xxxxx, (numeric) Sum of all tx sizes\n"
|
|
" \"usage\": xxxxx, (numeric) Total memory usage for the mempool\n"
|
|
" \"maxmempool\": xxxxx, (numeric) Maximum memory usage for the mempool\n"
|
|
" \"mempoolminfee\": xxxxx (numeric) Minimum fee for tx to be accepted\n"
|
|
"}\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("getmempoolinfo", "")
|
|
+ HelpExampleRpc("getmempoolinfo", "")
|
|
);
|
|
|
|
return mempoolInfoToJSON();
|
|
}
|
|
|
|
UniValue invalidateblock(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() != 1)
|
|
throw std::runtime_error(
|
|
"invalidateblock \"hash\"\n"
|
|
"\nPermanently marks a block as invalid, as if it violated a consensus rule.\n"
|
|
"\nArguments:\n"
|
|
"1. hash (string, required) the hash of the block to mark as invalid\n"
|
|
"\nResult:\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("invalidateblock", "\"blockhash\"")
|
|
+ HelpExampleRpc("invalidateblock", "\"blockhash\"")
|
|
);
|
|
|
|
std::string strHash = params[0].get_str();
|
|
uint256 hash(uint256S(strHash));
|
|
|
|
CBlockIndex* pblockindex = nullptr;
|
|
{
|
|
pblockindex = Blocks::Index::get(hash);
|
|
if (!pblockindex)
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
|
|
}
|
|
Application::instance()->validation()->invalidateBlock(pblockindex);
|
|
|
|
return NullUniValue;
|
|
}
|
|
|
|
UniValue reconsiderblock(const UniValue& params, bool fHelp)
|
|
{
|
|
if (fHelp || params.size() != 1)
|
|
throw std::runtime_error(
|
|
"reconsiderblock \"hash\"\n"
|
|
"\nRemoves invalidity status of a block and its descendants, reconsider them for activation.\n"
|
|
"This can be used to undo the effects of invalidateblock.\n"
|
|
"\nArguments:\n"
|
|
"1. hash (string, required) the hash of the block to reconsider\n"
|
|
"\nResult:\n"
|
|
"\nExamples:\n"
|
|
+ HelpExampleCli("reconsiderblock", "\"blockhash\"")
|
|
+ HelpExampleRpc("reconsiderblock", "\"blockhash\"")
|
|
);
|
|
|
|
std::string strHash = params[0].get_str();
|
|
uint256 hash(uint256S(strHash));
|
|
|
|
auto validator = Application::instance()->validation();
|
|
CBlockIndex* pblockindex = Blocks::Index::get(hash);
|
|
if (!pblockindex)
|
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
|
|
Blocks::Index::reconsiderBlock(pblockindex, validator->mempool()->utxo());
|
|
|
|
// try to actually validate the block again.
|
|
Validation::Settings future;
|
|
if (pblockindex->nStatus & BLOCK_HAVE_DATA) {
|
|
future = validator->addBlock(pblockindex->GetBlockPos()).start();
|
|
} else {
|
|
future = validator->addBlock(FastBlock::fromOldBlock(pblockindex->GetBlockHeader()), 0).start();
|
|
}
|
|
future.waitUntilFinished();
|
|
|
|
auto acceptedBlock = Blocks::DB::instance()->headerChain()[pblockindex->nHeight];
|
|
if (acceptedBlock && acceptedBlock != pblockindex)
|
|
logCritical(Log::RPC) << "Reconsider block done on block and alternate chain still wins. Consider invalidating block:"
|
|
<< acceptedBlock->GetBlockHash();
|
|
|
|
if (!future.error().empty())
|
|
throw JSONRPCError(RPC_DATABASE_ERROR, future.error());
|
|
return NullUniValue;
|
|
}
|