diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index de6bda4ea..4e55e0992 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -4,6 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "amount.h" +#include "base58.h" #include "chain.h" #include "chainparams.h" #include "checkpoints.h" @@ -13,16 +14,23 @@ #include "policy/policy.h" #include "primitives/transaction.h" #include "rpc/server.h" +#include "script/script.h" +#include "script/script_error.h" +#include "script/sign.h" +#include "script/standard.h" #include "streams.h" #include "sync.h" #include "txmempool.h" #include "util.h" #include "utilstrencodings.h" +#include "hash.h" #include #include +#include // boost::thread::interrupt + using namespace std; extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); @@ -37,7 +45,7 @@ double GetDifficulty(const CBlockIndex* blockindex) if (chainActive.Tip() == NULL) return 1.0; else - blockindex = chainActive.Tip(); + blockindex = GetLastBlockIndex(chainActive.Tip(), false); } int nShift = (blockindex->nBits >> 24) & 0xff; @@ -70,13 +78,123 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex) result.push_back(Pair("confirmations", confirmations)); result.push_back(Pair("height", blockindex->nHeight)); result.push_back(Pair("version", blockindex->nVersion)); + result.push_back(Pair("versionHex", strprintf("%08x", 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("mediantime", (int64_t)blockindex->GetPastTimeLimit())); 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())); + result.push_back(Pair("modifier", blockindex->nStakeModifier.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 blockToDeltasJSON(const CBlock& block, 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; + } else { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block is an orphan"); + } + 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 deltas(UniValue::VARR); + + for (unsigned int i = 0; i < block.vtx.size(); i++) { + const CTransaction &tx = block.vtx[i]; + const uint256 txhash = tx.GetHash(); + + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("txid", txhash.GetHex())); + entry.push_back(Pair("index", (int)i)); + + UniValue inputs(UniValue::VARR); + + if (!tx.IsCoinBase()) { + + for (size_t j = 0; j < tx.vin.size(); j++) { + const CTxIn input = tx.vin[j]; + + UniValue delta(UniValue::VOBJ); + + CSpentIndexValue spentInfo; + CSpentIndexKey spentKey(input.prevout.hash, input.prevout.n); + + if (GetSpentIndex(spentKey, spentInfo)) { + if (spentInfo.addressType == 1) { + delta.push_back(Pair("address", CBitcoinAddress(CKeyID(spentInfo.addressHash)).ToString())); + } else if (spentInfo.addressType == 2) { + delta.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString())); + } else { + continue; + } + delta.push_back(Pair("satoshis", -1 * spentInfo.satoshis)); + delta.push_back(Pair("index", (int)j)); + delta.push_back(Pair("prevtxid", input.prevout.hash.GetHex())); + delta.push_back(Pair("prevout", (int)input.prevout.n)); + + inputs.push_back(delta); + } else { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Spent information not available"); + } + + } + } + + entry.push_back(Pair("inputs", inputs)); + + UniValue outputs(UniValue::VARR); + + for (unsigned int k = 0; k < tx.vout.size(); k++) { + const CTxOut &out = tx.vout[k]; + + UniValue delta(UniValue::VOBJ); + + if (out.scriptPubKey.IsPayToScriptHash()) { + vector hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22); + delta.push_back(Pair("address", CBitcoinAddress(CScriptID(uint160(hashBytes))).ToString())); + + } else if (out.scriptPubKey.IsPayToPublicKeyHash()) { + vector hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23); + delta.push_back(Pair("address", CBitcoinAddress(CKeyID(uint160(hashBytes))).ToString())); + } else { + continue; + } + + delta.push_back(Pair("satoshis", out.nValue)); + delta.push_back(Pair("index", (int)k)); + + outputs.push_back(delta); + } + + entry.push_back(Pair("outputs", outputs)); + deltas.push_back(entry); + + } + result.push_back(Pair("deltas", deltas)); + result.push_back(Pair("time", block.GetBlockTime())); + result.push_back(Pair("mediantime", (int64_t)blockindex->GetPastTimeLimit())); + 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())); @@ -86,10 +204,45 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex) return result; } +double GetPoSKernelPS() +{ + int nPoSInterval = 72; + double dStakeKernelsTriedAvg = 0; + int nStakesHandled = 0, nStakesTime = 0; + + CBlockIndex* pindex = chainActive.Tip();; + CBlockIndex* pindexPrevStake = NULL; + + while (pindex && nStakesHandled < nPoSInterval) + { + if (pindex->IsProofOfStake()) + { + if (pindexPrevStake) + { + dStakeKernelsTriedAvg += GetDifficulty(pindexPrevStake) * 4294967296.0; + nStakesTime += pindexPrevStake->nTime - pindex->nTime; + nStakesHandled++; + } + pindexPrevStake = pindex; + } + + pindex = pindex->pprev; + } + + double result = 0; + + if (nStakesTime) + result = dStakeKernelsTriedAvg / nStakesTime; + + result *= 16; + + 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())); + 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)) @@ -98,6 +251,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx 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("versionHex", strprintf("%08x", block.nVersion))); result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); UniValue txs(UniValue::VARR); BOOST_FOREACH(const CTransaction&tx, block.vtx) @@ -113,7 +267,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx } 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("mediantime", (int64_t)blockindex->GetPastTimeLimit())); 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))); @@ -124,6 +278,10 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx CBlockIndex *pnext = chainActive.Next(blockindex); if (pnext) result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); + result.push_back(Pair("flags", blockindex->IsProofOfStake()? "proof-of-stake" : "proof-of-work")); + result.push_back(Pair("modifier", blockindex->nStakeModifier.GetHex())); + if (block.IsProofOfStake()) + result.push_back(Pair("signature", HexStr(block.vchBlockSig.begin(), block.vchBlockSig.end()))); return result; } @@ -163,19 +321,26 @@ UniValue getbestblockhash(const UniValue& params, bool fHelp) UniValue getdifficulty(const UniValue& params, bool fHelp) { - if (fHelp || params.size() != 0) - throw 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", "") - ); + if (fHelp || params.size() != 0) + throw runtime_error( + "getdifficulty\n" + "\nReturns the difficulty as a multiple of the minimum difficulty.\n" + "\nResult:\n" + "{ (json object)\n" + " \"proof-of-work\" : n.nnn, (numeric) the proof-of-work difficulty as a multiple of the minimum difficulty.\n" + " \"proof-of-stake\" : n.nnn (numeric) the proof-of-stake difficulty as a multiple of the minimum difficulty.\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getdifficulty", "") + + HelpExampleRpc("getdifficulty", "") + ); LOCK(cs_main); - return GetDifficulty(); + + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("proof-of-work", GetDifficulty())); + obj.push_back(Pair("proof-of-stake", GetDifficulty(GetLastBlockIndex(chainActive.Tip(), true)))); + return obj; } UniValue mempoolToJSON(bool fVerbose = false) @@ -266,8 +431,6 @@ UniValue getrawmempool(const UniValue& params, bool fHelp) + HelpExampleRpc("getrawmempool", "true") ); - LOCK(cs_main); - bool fVerbose = false; if (params.size() > 0) fVerbose = params[0].get_bool(); @@ -275,6 +438,102 @@ UniValue getrawmempool(const UniValue& params, bool fHelp) return mempoolToJSON(fVerbose); } +UniValue getblockdeltas(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error(""); + + std::string strHash = params[0].get_str(); + uint256 hash(uint256S(strHash)); + + if (mapBlockIndex.count(hash) == 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + + CBlock block; + CBlockIndex* pblockindex = mapBlockIndex[hash]; + + if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)"); + + if(!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); + + return blockToDeltasJSON(block, pblockindex); +} + +UniValue getblockhashes(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 2) + throw runtime_error( + "getblockhashes timestamp\n" + "\nReturns array of hashes of blocks within the timestamp range provided.\n" + "\nArguments:\n" + "1. high (numeric, required) The newer block timestamp\n" + "2. low (numeric, required) The older block timestamp\n" + "3. options (string, required) A json object\n" + " {\n" + " \"noOrphans\":true (boolean) will only include blocks on the main chain\n" + " \"logicalTimes\":true (boolean) will include logical timestamps with hashes\n" + " }\n" + "\nResult:\n" + "[\n" + " \"hash\" (string) The block hash\n" + "]\n" + "[\n" + " {\n" + " \"blockhash\": (string) The block hash\n" + " \"logicalts\": (numeric) The logical timestamp\n" + " }\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("getblockhashes", "1231614698 1231024505") + + HelpExampleRpc("getblockhashes", "1231614698, 1231024505") + + HelpExampleCli("getblockhashes", "1231614698 1231024505 '{\"noOrphans\":false, \"logicalTimes\":true}'") + ); + + unsigned int high = params[0].get_int(); + unsigned int low = params[1].get_int(); + bool fActiveOnly = false; + bool fLogicalTS = false; + + if (params.size() > 2) { + if (params[2].isObject()) { + UniValue noOrphans = find_value(params[2].get_obj(), "noOrphans"); + UniValue returnLogical = find_value(params[2].get_obj(), "logicalTimes"); + + if (noOrphans.isBool()) + fActiveOnly = noOrphans.get_bool(); + + if (returnLogical.isBool()) + fLogicalTS = returnLogical.get_bool(); + } + } + + std::vector > blockHashes; + + if (fActiveOnly) + LOCK(cs_main); + + if (!GetTimestampIndex(high, low, fActiveOnly, blockHashes)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for block hashes"); + } + + UniValue result(UniValue::VARR); + + for (std::vector >::const_iterator it=blockHashes.begin(); it!=blockHashes.end(); it++) { + if (fLogicalTS) { + UniValue item(UniValue::VOBJ); + item.push_back(Pair("blockhash", it->first.GetHex())); + item.push_back(Pair("logicalts", (int)it->second)); + result.push_back(item); + } else { + result.push_back(it->first.GetHex()); + } + } + + return result; +} + UniValue getblockhash(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) @@ -316,6 +575,7 @@ UniValue getblockheader(const UniValue& params, bool fHelp) " \"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" + " \"versionHex\" : \"00000000\", (string) The block version formatted in hexadecimal\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" @@ -375,6 +635,7 @@ UniValue getblock(const UniValue& params, bool fHelp) " \"size\" : n, (numeric) The block size\n" " \"height\" : n, (numeric) The block height or index\n" " \"version\" : n, (numeric) The block version\n" + " \"versionHex\" : \"00000000\", (string) The block version formatted in hexadecimal\n" " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" " \"tx\" : [ (array of string) The transaction ids\n" " \"transactionid\" (string) The transaction id\n" @@ -428,6 +689,60 @@ UniValue getblock(const UniValue& params, bool fHelp) return blockToJSON(block, pblockindex); } +struct CCoinsStats +{ + int nHeight; + uint256 hashBlock; + uint64_t nTransactions; + uint64_t nTransactionOutputs; + uint64_t nSerializedSize; + uint256 hashSerialized; + CAmount nTotalAmount; + + CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nSerializedSize(0), nTotalAmount(0) {} +}; + +//! Calculate statistics about the unspent transaction output set +static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats) +{ + boost::scoped_ptr pcursor(view->Cursor()); + + CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); + stats.hashBlock = pcursor->GetBestBlock(); + { + LOCK(cs_main); + stats.nHeight = mapBlockIndex.find(stats.hashBlock)->second->nHeight; + } + ss << stats.hashBlock; + CAmount nTotalAmount = 0; + while (pcursor->Valid()) { + boost::this_thread::interruption_point(); + uint256 key; + CCoins coins; + if (pcursor->GetKey(key) && pcursor->GetValue(coins)) { + stats.nTransactions++; + ss << key; + for (unsigned int i=0; iGetValueSize(); + ss << VARINT(0); + } else { + return error("%s: unable to read value", __func__); + } + pcursor->Next(); + } + stats.hashSerialized = ss.GetHash(); + stats.nTotalAmount = nTotalAmount; + return true; +} + UniValue gettxoutsetinfo(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) @@ -454,7 +769,7 @@ UniValue gettxoutsetinfo(const UniValue& params, bool fHelp) CCoinsStats stats; FlushStateToDisk(); - if (pcoinsTip->GetStats(stats)) { + if (GetUTXOStats(pcoinsTip, stats)) { ret.push_back(Pair("height", (int64_t)stats.nHeight)); ret.push_back(Pair("bestblock", stats.hashBlock.GetHex())); ret.push_back(Pair("transactions", (int64_t)stats.nTransactions)); @@ -493,6 +808,7 @@ UniValue gettxout(const UniValue& params, bool fHelp) " },\n" " \"version\" : n, (numeric) The version\n" " \"coinbase\" : true|false (boolean) Coinbase or not\n" + " \"coinstake\" : true|false (boolean) Coinstake or not\n" "}\n" "\nExamples:\n" @@ -542,6 +858,7 @@ UniValue gettxout(const UniValue& params, bool fHelp) ret.push_back(Pair("scriptPubKey", o)); ret.push_back(Pair("version", coins.nVersion)); ret.push_back(Pair("coinbase", coins.fCoinBase)); + ret.push_back(Pair("coinstake", coins.fCoinStake)); return ret; } @@ -604,6 +921,35 @@ static UniValue SoftForkDesc(const std::string &name, int version, CBlockIndex* return rv; } +static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Consensus::DeploymentPos id) +{ + UniValue rv(UniValue::VOBJ); + const ThresholdState thresholdState = VersionBitsTipState(consensusParams, id); + switch (thresholdState) { + case THRESHOLD_DEFINED: rv.push_back(Pair("status", "defined")); break; + case THRESHOLD_STARTED: rv.push_back(Pair("status", "started")); break; + case THRESHOLD_LOCKED_IN: rv.push_back(Pair("status", "locked_in")); break; + case THRESHOLD_ACTIVE: rv.push_back(Pair("status", "active")); break; + case THRESHOLD_FAILED: rv.push_back(Pair("status", "failed")); break; + } + if (THRESHOLD_STARTED == thresholdState) + { + rv.push_back(Pair("bit", consensusParams.vDeployments[id].bit)); + } + rv.push_back(Pair("startTime", consensusParams.vDeployments[id].nStartTime)); + rv.push_back(Pair("timeout", consensusParams.vDeployments[id].nTimeout)); + return rv; +} + +void BIP9SoftForkDescPushBack(UniValue& bip9_softforks, const std::string &name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) +{ + // Deployments with timeout value of 0 are hidden. + // A timeout value of 0 guarantees a softfork will never be activated. + // This is used when softfork codes are merged without specifying the deployment schedule. + if (consensusParams.vDeployments[id].nTimeout > 0) + bip9_softforks.push_back(Pair(name, BIP9SoftForkDesc(consensusParams, id))); +} + UniValue getblockchaininfo(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) @@ -616,7 +962,10 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) " \"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" + " \"difficulty\": { (json object)\n" + " \"proof-of-work\": xxxxxx, (numeric) the current proof-of-work difficulty\n" + " \"proof-of-stake\": xxxxxx (numeric) the current proof-of-stake difficulty\n" + " },\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" @@ -634,7 +983,15 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) " },\n" " \"reject\": { ... } (object) progress toward rejecting pre-softfork blocks (same fields as \"enforce\")\n" " }, ...\n" - " ]\n" + " ],\n" + " \"bip9_softforks\": { (object) status of BIP9 softforks in progress\n" + " \"xxxx\" : { (string) name of the softfork\n" + " \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n" + " \"bit\": xx, (numeric) the bit, 0-28, in the block version field used to signal this soft fork\n" + " \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n" + " \"timeout\": xx (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n" + " }\n" + " }\n" "}\n" "\nExamples:\n" + HelpExampleCli("getblockchaininfo", "") @@ -643,13 +1000,17 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) LOCK(cs_main); + UniValue diff(UniValue::VOBJ); + diff.push_back(Pair("proof-of-work", (double)GetDifficulty())); + diff.push_back(Pair("proof-of-stake", (double)GetDifficulty(GetLastBlockIndex(chainActive.Tip(), true)))); + 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("difficulty", diff)); + obj.push_back(Pair("mediantime", (int64_t)chainActive.Tip()->GetPastTimeLimit())); obj.push_back(Pair("verificationprogress", Checkpoints::GuessVerificationProgress(Params().Checkpoints(), chainActive.Tip()))); obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex())); obj.push_back(Pair("pruned", fPruneMode)); @@ -657,10 +1018,10 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) const Consensus::Params& consensusParams = Params().GetConsensus(); CBlockIndex* tip = chainActive.Tip(); UniValue softforks(UniValue::VARR); - softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams)); - softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams)); - softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams)); + UniValue bip9_softforks(UniValue::VOBJ); + BIP9SoftForkDescPushBack(bip9_softforks, "csv", consensusParams, Consensus::DEPLOYMENT_CSV); obj.push_back(Pair("softforks", softforks)); + obj.push_back(Pair("bip9_softforks", bip9_softforks)); if (fPruneMode) { @@ -723,17 +1084,30 @@ UniValue getchaintips(const UniValue& params, bool fHelp) 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. */ + /* + * Idea: the set of chain tips is chainActive.tip, plus orphan blocks which do not have another orphan building off of them. + * Algorithm: + * - Make one pass through mapBlockIndex, picking out the orphan blocks, and also storing a set of the orphan block's pprev pointers. + * - Iterate through the orphan blocks. If the block isn't pointed to by another orphan, it is a chain tip. + * - add chainActive.Tip() + */ std::set setTips; - BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex) - setTips.insert(item.second); + std::set setOrphans; + std::set setPrevs; + BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex) { - const CBlockIndex* pprev = item.second->pprev; - if (pprev) - setTips.erase(pprev); + if (!chainActive.Contains(item.second)) { + setOrphans.insert(item.second); + setPrevs.insert(item.second->pprev); + } + } + + for (std::set::iterator it = setOrphans.begin(); it != setOrphans.end(); ++it) + { + if (setPrevs.erase(*it) == 0) { + setTips.insert(*it); + } } // Always report the currently active tip. diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 996b7f9cb..0fe790413 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -14,11 +14,16 @@ #include "miner.h" #include "net.h" #include "pow.h" +#include "pos.h" #include "rpc/server.h" #include "txmempool.h" +#include "timedata.h" #include "util.h" #include "utilstrencodings.h" #include "validationinterface.h" +#ifdef ENABLE_WALLET +#include "wallet/wallet.h" +#endif #include @@ -68,7 +73,7 @@ UniValue GetNetworkHashPS(int lookup, int height) { arith_uint256 workDiff = pb->nChainWork - pb0->nChainWork; int64_t timeDiff = maxTime - minTime; - return (int64_t)(workDiff.getdouble() / timeDiff); + return workDiff.getdouble() / timeDiff; } UniValue getnetworkhashps(const UniValue& params, bool fHelp) @@ -157,7 +162,7 @@ UniValue generate(const UniValue& params, bool fHelp) UniValue blockHashes(UniValue::VARR); while (nHeight < nHeightEnd) { - auto_ptr pblocktemplate(CreateNewBlock(Params(), coinbaseScript->reserveScript)); + auto_ptr pblocktemplate(CreateNewBlock(Params(), coinbaseScript->reserveScript, 0, false)); if (!pblocktemplate.get()) throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); CBlock *pblock = &pblocktemplate->block; @@ -165,16 +170,17 @@ UniValue generate(const UniValue& params, bool fHelp) LOCK(cs_main); IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce); } - while (!CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) { + while (!CheckProofOfWork(pblock->GetPoWHash(), pblock->nBits, Params().GetConsensus())) { // Yes, there is a chance every nonce could fail to satisfy the -regtest // target -- 1 in 2^(2^32). That ain't gonna happen. ++pblock->nNonce; } CValidationState state; - if (!ProcessNewBlock(state, Params(), NULL, pblock, true, NULL)) + uint256 hash = pblock->GetHash(); + if (!ProcessNewBlock(state, Params(), NULL, pblock, true, NULL, hash)) throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted"); ++nHeight; - blockHashes.push_back(pblock->GetHash().GetHex()); + blockHashes.push_back(hash.GetHex()); //mark script as important because it was used at least for one coinbase output coinbaseScript->KeepScript(); @@ -268,6 +274,42 @@ UniValue getmininginfo(const UniValue& params, bool fHelp) return obj; } +UniValue getstakinginfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getstakinginfo\n" + "Returns an object containing staking-related information."); + + uint64_t nWeight = 0; + if (pwalletMain) + nWeight = pwalletMain->GetStakeWeight(); + + uint64_t nNetworkWeight = GetPoSKernelPS(); + bool staking = nLastCoinStakeSearchInterval && nWeight; + uint64_t nExpectedTime = staking ? 1.0455 * 64 * nNetworkWeight / nWeight : 0; + + UniValue obj(UniValue::VOBJ); + + obj.push_back(Pair("enabled", GetBoolArg("-staking", true))); + obj.push_back(Pair("staking", staking)); + obj.push_back(Pair("errors", GetWarnings("statusbar"))); + + obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize)); + obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx)); + obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); + + obj.push_back(Pair("difficulty", GetDifficulty(GetLastBlockIndex(chainActive.Tip(), true)))); + obj.push_back(Pair("search-interval", (int)nLastCoinStakeSearchInterval)); + + obj.push_back(Pair("weight", (uint64_t)nWeight)); + obj.push_back(Pair("netstakeweight", (uint64_t)nNetworkWeight)); + + obj.push_back(Pair("expectedtime", nExpectedTime)); + + return obj; +} + // NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts UniValue prioritisetransaction(const UniValue& params, bool fHelp) @@ -426,7 +468,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) if (block.hashPrevBlock != pindexPrev->GetBlockHash()) return "inconclusive-not-best-prevblk"; CValidationState state; - TestBlockValidity(state, Params(), block, pindexPrev, false, true); + TestBlockValidity(state, Params(), block, pindexPrev, false, true, true); return BIP22ValidationResult(state); } } @@ -440,6 +482,9 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) if (IsInitialBlockDownload()) throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Bitcoin is downloading blocks..."); + if (chainActive.Tip()->nHeight > Params().GetConsensus().nLastPOWBlock) + throw JSONRPCError(RPC_MISC_ERROR, "No more PoW blocks"); + static unsigned int nTransactionsUpdatedLast; if (!lpval.isNull()) @@ -510,7 +555,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) pblocktemplate = NULL; } CScript scriptDummy = CScript() << OP_TRUE; - pblocktemplate = CreateNewBlock(Params(), scriptDummy); + pblocktemplate = CreateNewBlock(Params(), scriptDummy, 0, false); if (!pblocktemplate) throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); @@ -578,7 +623,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) 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("mintime", (int64_t)pindexPrev->GetPastTimeLimit()+1)); result.push_back(Pair("mutable", aMutable)); result.push_back(Pair("noncerange", "00000000ffffffff")); result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS)); @@ -650,9 +695,9 @@ UniValue submitblock(const UniValue& params, bool fHelp) } CValidationState state; - submitblock_StateCatcher sc(block.GetHash()); + submitblock_StateCatcher sc(hash); RegisterValidationInterface(&sc); - bool fAccepted = ProcessNewBlock(state, Params(), NULL, &block, true, NULL); + bool fAccepted = ProcessNewBlock(state, Params(), NULL, &block, true, NULL, hash); UnregisterValidationInterface(&sc); if (fBlockPresent) { @@ -700,6 +745,128 @@ UniValue estimatefee(const UniValue& params, bool fHelp) return ValueFromAmount(feeRate.GetFeePerK()); } +UniValue checkkernel(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "checkkernel [{\"txid\":txid,\"vout\":n},...] [createblocktemplate=false]\n" + "Check if one of given inputs is a kernel input at the moment.\n" + ); + + RPCTypeCheck(params, boost::assign::list_of(UniValue::VARR)(UniValue::VBOOL)); + + UniValue inputs = params[0].get_array(); + bool fCreateBlockTemplate = params.size() > 1 ? params[1].get_bool() : false; + + if (vNodes.empty()) + throw JSONRPCError(-9, "BlackCoin is not connected!"); + + if (IsInitialBlockDownload()) + throw JSONRPCError(-10, "BlackCoin is downloading blocks..."); + + COutPoint kernel; + CBlockIndex* pindexPrev = chainActive.Tip(); + CBlockHeader blockHeader = pindexPrev->GetBlockHeader(); + unsigned int nBits = GetNextTargetRequired(pindexPrev, &blockHeader, true, Params().GetConsensus()); + int64_t nTime = GetAdjustedTime(); + nTime &= ~Params().GetConsensus().nStakeTimestampMask; + + for (unsigned int idx = 0; idx < inputs.size(); idx++) { + const UniValue& input = inputs[idx]; + const UniValue& o = input.get_obj(); + + const UniValue& txid_v = find_value(o, "txid"); + if (!txid_v.isStr()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing txid key"); + string txid = txid_v.get_str(); + if (!IsHex(txid)) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid"); + + const UniValue& vout_v = find_value(o, "vout"); + if (!vout_v.isNum()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); + int nOutput = vout_v.get_int(); + if (nOutput < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); + + COutPoint cInput(uint256S(txid), nOutput); + if (CheckKernel(pindexPrev, nBits, nTime, cInput)) + { + kernel = cInput; + break; + } + } + + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("found", !kernel.IsNull())); + + if (kernel.IsNull()) + return result; + + UniValue oKernel(UniValue::VOBJ); + oKernel.push_back(Pair("txid", kernel.hash.GetHex())); + oKernel.push_back(Pair("vout", (int64_t)kernel.n)); + oKernel.push_back(Pair("time", nTime)); + result.push_back(Pair("kernel", oKernel)); + + if (!fCreateBlockTemplate) + return result; + + int64_t nFees; + if (!pwalletMain->IsLocked()) + pwalletMain->TopUpKeyPool(); + + CReserveKey pMiningKey(pwalletMain); + auto_ptr pblocktemplate(CreateNewBlock(Params(), pMiningKey.reserveScript, &nFees, true)); + if (!pblocktemplate.get()) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); + + CBlock *pblock = &pblocktemplate->block; + pblock->nTime = pblock->vtx[0].nTime = nTime; + + CDataStream ss(SER_DISK, PROTOCOL_VERSION); + ss << *pblock; + + result.push_back(Pair("blocktemplate", HexStr(ss.begin(), ss.end()))); + result.push_back(Pair("blocktemplatefees", nFees)); + + CPubKey pubkey; + if (!pMiningKey.GetReservedKey(pubkey)) + throw JSONRPCError(RPC_MISC_ERROR, "GetReservedKey failed"); + + result.push_back(Pair("blocktemplatesignkey", HexStr(pubkey))); + + return result; + if (fHelp || params.size() != 1) + throw runtime_error( + "estimatefee nblocks\n" + "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n" + "confirmation within nblocks blocks.\n" + "\nArguments:\n" + "1. nblocks (numeric)\n" + "\nResult:\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" + "\nExample:\n" + + HelpExampleCli("estimatefee", "6") + ); + + RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); + + int nBlocks = params[0].get_int(); + if (nBlocks < 1) + nBlocks = 1; + + CFeeRate feeRate = mempool.estimateFee(nBlocks); + if (feeRate == CFeeRate(0)) + return -1.0; + + return ValueFromAmount(feeRate.GetFeePerK()); +} + UniValue estimatepriority(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1)