diff --git a/src/init.cpp b/src/init.cpp index b47c29921d..bf8ae9a47c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1476,6 +1476,7 @@ bool AppInitMain(InitInterfaces& interfaces) int64_t nTotalCache = (gArgs.GetArg("-dbcache", nDefaultDbCache) << 20); nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greater than nMaxDbcache + auto nCustomCacheSize = nTotalCache; // used for criminals and customs int64_t nBlockTreeDBCache = std::min(nTotalCache / 8, nMaxBlockDBCache << 20); nTotalCache -= nBlockTreeDBCache; int64_t nTxIndexCache = std::min(nTotalCache / 8, gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxTxIndexCache << 20 : 0); @@ -1491,6 +1492,7 @@ bool AppInitMain(InitInterfaces& interfaces) nCoinDBCache = std::min(nCoinDBCache, nMaxCoinsDBCache << 20); // cap total coins db cache nTotalCache -= nCoinDBCache; nCoinCacheUsage = nTotalCache; // the rest goes to in-memory cache + nCustomMemUsage = std::max((nTotalCache >> 8), (nMinDbCache << 16)); // use significant less in-memory cache int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; LogPrintf("Cache configuration:\n"); LogPrintf("* Using %.1f MiB for block index database\n", nBlockTreeDBCache * (1.0 / 1024 / 1024)); @@ -1587,10 +1589,10 @@ bool AppInitMain(InitInterfaces& interfaces) }); pcriminals.reset(); - pcriminals = MakeUnique(GetDataDir() / "criminals", nDefaultDbCache << 20, false, fReset || fReindexChainState); + pcriminals = MakeUnique(GetDataDir() / "criminals", nCustomCacheSize, false, fReset || fReindexChainState); pcustomcsDB.reset(); - pcustomcsDB = MakeUnique(GetDataDir() / "enhancedcs", nDefaultDbCache << 20, false, fReset || fReindexChainState); + pcustomcsDB = MakeUnique(GetDataDir() / "enhancedcs", nCustomCacheSize, false, fReset || fReindexChainState); pcustomcsview.reset(); pcustomcsview = MakeUnique(*pcustomcsDB.get()); if (!fReset && (gArgs.GetBoolArg("-acindex", DEFAULT_ACINDEX) || gArgs.GetBoolArg("-acindex-mineonly", DEFAULT_ACINDEX_MINEONLY))) { diff --git a/src/masternodes/masternodes.cpp b/src/masternodes/masternodes.cpp index f5419fc9bc..8d287818ba 100644 --- a/src/masternodes/masternodes.cpp +++ b/src/masternodes/masternodes.cpp @@ -43,28 +43,6 @@ const unsigned char CAnchorConfirmsView::BtcTx::prefix = DB_MN_ANCHOR_CONFIRM; const unsigned char CTeamView::AuthTeam ::prefix = DB_MN_AUTH_TEAM; const unsigned char CTeamView::ConfirmTeam ::prefix = DB_MN_CONFIRM_TEAM; -struct MNBlockTimeKey -{ - uint256 masternodeID; - uint32_t blockHeight; - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(masternodeID); - - if (ser_action.ForRead()) { - READWRITE(WrapBigEndian(blockHeight)); - blockHeight = ~blockHeight; - } - else { - uint32_t blockHeight_ = ~blockHeight; - READWRITE(WrapBigEndian(blockHeight_)); - } - } -}; - std::unique_ptr pcustomcsview; std::unique_ptr pcustomcsDB; @@ -409,7 +387,7 @@ boost::optional CMasternodesView::GetMasternodeLastBlockTime(const CKey int64_t time{0}; - ForEach([&](const MNBlockTimeKey &key, const int64_t &blockTime) + ForEachMinterNode([&](const MNBlockTimeKey &key, int64_t blockTime) { if (key.masternodeID == nodeId) { @@ -433,6 +411,11 @@ void CMasternodesView::EraseMasternodeLastBlockTime(const uint256& nodeId, const EraseBy(MNBlockTimeKey{nodeId, blockHeight}); } +void CMasternodesView::ForEachMinterNode(std::function)> callback, MNBlockTimeKey const & start) +{ + ForEach(callback, start); +} + //void CMasternodesView::UnCreateMasternode(const uint256 & nodeId) //{ // auto node = GetMasternode(nodeId); diff --git a/src/masternodes/masternodes.h b/src/masternodes/masternodes.h index 7a50f05864..d7a6a7d247 100644 --- a/src/masternodes/masternodes.h +++ b/src/masternodes/masternodes.h @@ -111,6 +111,27 @@ class CMasternode }; +struct MNBlockTimeKey +{ + uint256 masternodeID; + uint32_t blockHeight; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(masternodeID); + + if (ser_action.ForRead()) { + READWRITE(WrapBigEndian(blockHeight)); + blockHeight = ~blockHeight; + } else { + uint32_t blockHeight_ = ~blockHeight; + READWRITE(WrapBigEndian(blockHeight_)); + } + } +}; + class CMasternodesView : public virtual CStorageView { public: @@ -142,6 +163,8 @@ class CMasternodesView : public virtual CStorageView boost::optional GetMasternodeLastBlockTime(const CKeyID & minter); void EraseMasternodeLastBlockTime(const uint256 &minter, const uint32_t& blockHeight); + void ForEachMinterNode(std::function)> callback, MNBlockTimeKey const & start = {}); + // tags struct ID { static const unsigned char prefix; }; struct Operator { static const unsigned char prefix; }; diff --git a/src/masternodes/rpc_accounts.cpp b/src/masternodes/rpc_accounts.cpp index db49f2f822..415a610036 100644 --- a/src/masternodes/rpc_accounts.cpp +++ b/src/masternodes/rpc_accounts.cpp @@ -76,16 +76,22 @@ UniValue rewardhistoryToJSON(RewardHistoryKey const & key, std::pairheight); obj.pushKV("blockHash", index->GetBlockHash().GetHex()); obj.pushKV("blockTime", index->GetBlockTime()); - obj.pushKV("type", type); + if (pwtx->IsCoinBase()) { + obj.pushKV("type", "blockReward"); + } else if (entry.amount < 0) { + obj.pushKV("type", "sent"); + } else { + obj.pushKV("type", "receive"); + } obj.pushKV("txn", (uint64_t) entry.vout); - obj.pushKV("txid", txid.ToString()); + obj.pushKV("txid", pwtx->GetHash().ToString()); obj.pushKV("amounts", AmountsToJSON({{DCT_ID{0}, entry.amount}})); return obj; } @@ -93,9 +99,8 @@ UniValue outputEntryToJSON(COutputEntry const & entry, CBlockIndex const * index static void searchInWallet(CWallet const * pwallet, CScript const & account, isminetype filter, - std::function shouldSkipTx, - std::function onSent, - std::function onReceive) { + std::function shouldSkipTx, + std::function txEntry) { CTxDestination destination; ExtractDestination(account, destination); @@ -111,11 +116,12 @@ static void searchInWallet(CWallet const * pwallet, for (const auto& tx : txOrdered) { auto* pwtx = &tx; - if (pwtx->IsCoinBase()) { + auto index = LookupBlockIndex(pwtx->hashBlock); + if (!index || index->height == 0) { // skip genesis block continue; } - if (shouldSkipTx(pwtx)) { + if (shouldSkipTx(index, pwtx)) { continue; } @@ -129,7 +135,7 @@ static void searchInWallet(CWallet const * pwallet, continue; } sent.amount = -sent.amount; - if (!onSent(sent)) { + if (!txEntry(sent, index, pwtx)) { return; } } @@ -141,7 +147,7 @@ static void searchInWallet(CWallet const * pwallet, if (IsValidDestination(destination) && destination != recv.destination) { continue; } - if (!onReceive(recv)) { + if (!txEntry(recv, index, pwtx)) { return; } } @@ -983,31 +989,17 @@ UniValue listaccounthistory(const JSONRPCRequest& request) { } if (shouldSearchInWallet) { - uint256 txid; - CBlockIndex const * index; - auto insertEntry = [&](COutputEntry const & entry, std::string const & info) -> bool { - auto& array = ret.emplace(index->height, UniValue::VARR).first->second; - array.push_back(outputEntryToJSON(entry, index, txid, info)); - return --count != 0; - }; - searchInWallet(pwallet, account, filter, [&](CWalletTx const * pwtx) -> bool { - txid = pwtx->GetHash(); - if (txs.count(txid)) { - return true; - } - - // Check we have index before progressing, wallet might be reindexing. - if (!(index = LookupBlockIndex(pwtx->hashBlock))) { - return true; - } - - if (startBlock > index->height || index->height > maxBlockHeight) { - return true; + count = limit; + searchInWallet(pwallet, account, filter, + [&](CBlockIndex const * index, CWalletTx const * pwtx) { + return txs.count(pwtx->GetHash()) || startBlock > index->height || index->height > maxBlockHeight; + }, + [&](COutputEntry const & entry, CBlockIndex const * index, CWalletTx const * pwtx) { + auto& array = ret.emplace(index->height, UniValue::VARR).first->second; + array.push_back(outputEntryToJSON(entry, index, pwtx)); + return --count != 0; } - - return false; - }, std::bind(insertEntry, std::placeholders::_1, "sent"), - std::bind(insertEntry, std::placeholders::_1, "receive")); + ); } if (!noRewards) { @@ -1203,25 +1195,15 @@ UniValue accounthistorycount(const JSONRPCRequest& request) { } if (shouldSearchInWallet) { - auto incCount = [&count](COutputEntry const &) { ++count; return true; }; - searchInWallet(pwallet, owner, filter, [&](CWalletTx const * pwtx) -> bool { - if (txs.count(pwtx->GetHash())) { - return true; - } - - auto index = LookupBlockIndex(pwtx->hashBlock); - - // Check we have index before progressing, wallet might be reindexing. - if (!index) { + searchInWallet(pwallet, owner, filter, + [&](CBlockIndex const * index, CWalletTx const * pwtx) { + return txs.count(pwtx->GetHash()) || index->height > currentHeight; + }, + [&count](COutputEntry const &, CBlockIndex const *, CWalletTx const *) { + ++count; return true; } - - if (index->height > currentHeight) { - return true; - } - - return false; - }, incCount, incCount); + ); } if (noRewards) { diff --git a/src/masternodes/rpc_masternodes.cpp b/src/masternodes/rpc_masternodes.cpp index f61063dd1b..7c4c9035a3 100644 --- a/src/masternodes/rpc_masternodes.cpp +++ b/src/masternodes/rpc_masternodes.cpp @@ -354,18 +354,17 @@ UniValue getmasternodeblocks(const JSONRPCRequest& request) { UniValue identifier = request.params[0].get_obj(); int idCount{0}; uint256 mn_id; - std::string ownerAddress; - std::string operatorAddress; - CKeyID ownerAddressID; - CKeyID operatorAddressID; if (!identifier["id"].isNull()) { mn_id = ParseHashV(identifier["id"], "id"); ++idCount; } + LOCK(cs_main); + if (!identifier["ownerAddress"].isNull()) { - ownerAddress = identifier["ownerAddress"].getValStr(); + CKeyID ownerAddressID; + auto ownerAddress = identifier["ownerAddress"].getValStr(); auto ownerDest = DecodeDestination(ownerAddress); if (ownerDest.which() == 1) { ownerAddressID = CKeyID(*boost::get(&ownerDest)); @@ -374,11 +373,17 @@ UniValue getmasternodeblocks(const JSONRPCRequest& request) { } else { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid P2PKH address"); } + auto node = pcustomcsview->GetMasternodeIdByOwner(ownerAddressID); + if (!node) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Masternode not found"); + } + mn_id = *node; ++idCount; } if (!identifier["operatorAddress"].isNull()) { - operatorAddress = identifier["operatorAddress"].getValStr(); + CKeyID operatorAddressID; + auto operatorAddress = identifier["operatorAddress"].getValStr(); auto operatorDest = DecodeDestination(operatorAddress); if (operatorDest.which() == 1) { operatorAddressID = CKeyID(*boost::get(&operatorDest)); @@ -387,6 +392,11 @@ UniValue getmasternodeblocks(const JSONRPCRequest& request) { } else { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid P2PKH address"); } + auto node = pcustomcsview->GetMasternodeIdByOperator(operatorAddressID); + if (!node) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Masternode not found"); + } + mn_id = *node; ++idCount; } @@ -398,50 +408,42 @@ UniValue getmasternodeblocks(const JSONRPCRequest& request) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Only provide one identifier information"); } - LOCK(cs_main); - if (mn_id != uint256()) { - auto node = pcustomcsview->GetMasternode(mn_id); - if (!node) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Masternode not found"); - } - } else if (!ownerAddress.empty()) { - auto node = pcustomcsview->GetMasternodeIdByOwner(ownerAddressID); - if (!node) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Masternode not found"); - } - } else if (!operatorAddress.empty()) { - auto node = pcustomcsview->GetMasternodeIdByOperator(operatorAddressID); - if (!node) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Masternode not found"); - } + auto masternode = pcustomcsview->GetMasternode(mn_id); + if (!masternode) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Masternode not found"); } + auto lastHeight = ::ChainActive().Tip()->height + 1; + const auto creationHeight = masternode->creationHeight; + int depth{std::numeric_limits::max()}; if (!request.params[1].isNull()) { depth = request.params[1].get_int(); } - const CBlockIndex* tip = ::ChainActive().Tip(); - UniValue ret(UniValue::VOBJ); - for (; tip && depth > 0; tip = tip->pprev, --depth) { + pcustomcsview->ForEachMinterNode([&](MNBlockTimeKey const & key, CLazySerialize) { + if (key.masternodeID != mn_id) { + return false; + } + + if (auto tip = ::ChainActive()[key.blockHeight]) { + lastHeight = tip->height; + ret.pushKV(std::to_string(tip->height), tip->GetBlockHash().ToString()); + } + + return true; + }, MNBlockTimeKey{mn_id, std::numeric_limits::max()}); + + auto tip = ::ChainActive()[std::min(lastHeight, uint64_t(Params().GetConsensus().DakotaCrescentHeight)) - 1]; + + for (; tip && tip->height > creationHeight && depth > 0; tip = tip->pprev, --depth) { CKeyID minter; if (tip->GetBlockHeader().ExtractMinterKey(minter)) { auto id = pcustomcsview->GetMasternodeIdByOperator(minter); - if (id) { - if (mn_id != uint256()) { - if (id == mn_id) { - ret.pushKV(std::to_string(tip->height), tip->GetBlockHash().ToString()); - } - } else if (!ownerAddress.empty()) { - auto mn = pcustomcsview->GetMasternode(*id); - if (mn->ownerAuthAddress == ownerAddressID) { - ret.pushKV(std::to_string(tip->height), tip->GetBlockHash().ToString()); - } - } else if (minter == operatorAddressID) { - ret.pushKV(std::to_string(tip->height), tip->GetBlockHash().ToString()); - } + if (id && *id == mn_id) { + ret.pushKV(std::to_string(tip->height), tip->GetBlockHash().ToString()); } } } diff --git a/src/miner.cpp b/src/miner.cpp index c28d2bd8c3..889ab09d65 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -121,6 +121,12 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc if (!nodePtr || !nodePtr->IsActive()) return nullptr; + // update last block creation attempt ts for the master node + { + CLockFreeGuard lock(pos::cs_MNLastBlockCreationAttemptTs); + pos::mapMNLastBlockCreationAttemptTs[myIDs->second] = GetTime(); + } + CBlockIndex* pindexPrev = ::ChainActive().Tip(); assert(pindexPrev != nullptr); nHeight = pindexPrev->nHeight + 1; diff --git a/src/miner.h b/src/miner.h index faeb4260fe..98338fe18e 100644 --- a/src/miner.h +++ b/src/miner.h @@ -246,6 +246,11 @@ namespace pos { template bool withSearchInterval(F&& f); }; + + // Map to store [master node id : last block creation attempt timestamp] for local master nodes + static std::map mapMNLastBlockCreationAttemptTs; + static std::atomic_bool cs_MNLastBlockCreationAttemptTs(false); + } #endif // DEFI_MINER_H diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 2694c2b5ec..12ae5664da 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -217,10 +217,11 @@ static UniValue generatetoaddress(const JSONRPCRequest& request) return generateBlocks(coinbase_script, minterKey, myIDs->first, nGenerate, nMaxTries); } +/// @deprecated version of getmininginfo. prefer using getmininginfo static UniValue getmintinginfo(const JSONRPCRequest& request) { RPCHelpMan{"getmintinginfo", - "\nReturns a json object containing mining-related information.", + "\nDEPRECATED. Prefer using getmininginfo.\nReturns a json object containing mining-related information.", {}, RPCResult{ "{\n" @@ -267,6 +268,76 @@ static UniValue getmintinginfo(const JSONRPCRequest& request) return obj; } +// Returns the mining information of all local masternodes +static UniValue getmininginfo(const JSONRPCRequest& request) +{ + RPCHelpMan{"getmininginfo", + "\nReturns a json object containing mining-related information for all local masternodes", + {}, + RPCResult{ + "{\n" + " \"blocks\": nnn, (numeric) The current block\n" + " \"currentblockweight\": nnn, (numeric, optional) The block weight of the last assembled block (only present if a block was ever assembled)\n" + " \"currentblocktx\": nnn, (numeric, optional) The number of block transactions of the last assembled block (only present if a block was ever assembled)\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" + " \"isoperator\": true|false (boolean) Local master nodes are available or not \n" + " \"masternodes\": [] (array) an array of objects which includes each master node information\n" + " \"warnings\": \"...\" (string) any network and blockchain warnings\n" + "}\n" + }, + RPCExamples{ + HelpExampleCli("getmininginfo", "") + + HelpExampleRpc("getmininginfo", "") + }, + }.Check(request); + + LOCK(cs_main); + + UniValue obj(UniValue::VOBJ); + obj.pushKV("blocks", (int)::ChainActive().Height()); + if (BlockAssembler::m_last_block_weight) obj.pushKV("currentblockweight", *BlockAssembler::m_last_block_weight); + if (BlockAssembler::m_last_block_num_txs) obj.pushKV("currentblocktx", *BlockAssembler::m_last_block_num_txs); + obj.pushKV("difficulty", (double)GetDifficulty(::ChainActive().Tip())); + obj.pushKV("networkhashps", getnetworkhashps(request)); + obj.pushKV("pooledtx", (uint64_t)mempool.size()); + obj.pushKV("chain", Params().NetworkIDString()); + + // get all masternode operators + auto mnIds = pcustomcsview->GetOperatorsMulti(); + obj.pushKV("isoperator", !mnIds.empty()); + + UniValue mnArr(UniValue::UniValue::VARR); // masternodes array + for (const auto& mnId : mnIds) { + UniValue subObj(UniValue::VOBJ); + + subObj.pushKV("masternodeid", mnId.second.GetHex()); + CMasternode const & node = *pcustomcsview->GetMasternode(mnId.second); + auto state = node.GetState(); + subObj.pushKV("masternodeoperator", node.operatorAuthAddress.GetHex());// NOTE(sp) : Should this also be encoded? not the HEX + subObj.pushKV("masternodestate", CMasternode::GetHumanReadableState(state)); + auto generate = node.IsActive() && gArgs.GetBoolArg("-gen", DEFAULT_GENERATE); + subObj.pushKV("generate", generate); + subObj.pushKV("mintedblocks", (uint64_t)node.mintedBlocks); + + if (!generate) { + subObj.pushKV("lastblockcreationattempt", "0"); + } else { + // get the last block creation attempt by the master node + CLockFreeGuard lock(pos::cs_MNLastBlockCreationAttemptTs); + auto lastBlockCreationAttemptTs = pos::mapMNLastBlockCreationAttemptTs[mnId.second]; + subObj.pushKV("lastblockcreationattempt", (lastBlockCreationAttemptTs != 0) ? FormatISO8601DateTime(lastBlockCreationAttemptTs) : "0"); + } + + mnArr.push_back(subObj); + } + + obj.pushKV("masternodes", mnArr); + obj.pushKV("warnings", GetWarnings("statusbar")); + return obj; +} // NOTE: Unlike wallet RPC (which use DFI values), mining RPCs follow GBT (BIP 22) in using satoshi amounts static UniValue prioritisetransaction(const JSONRPCRequest& request) @@ -1011,6 +1082,7 @@ static const CRPCCommand commands[] = { "mining", "getblocktemplate", &getblocktemplate, {"template_request"} }, { "mining", "submitblock", &submitblock, {"hexdata","dummy"} }, { "mining", "submitheader", &submitheader, {"hexdata"} }, + { "mining", "getmininginfo", &getmininginfo, {} }, { "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} }, diff --git a/src/validation.cpp b/src/validation.cpp index 6edcb5e026..d6b5333337 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -129,6 +129,7 @@ bool fRequireStandard = true; bool fCheckBlockIndex = false; bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED; size_t nCoinCacheUsage = 5000 * 300; +size_t nCustomMemUsage = nDefaultDbCache << 10; uint64_t nPruneTarget = 0; bool fIsFakeNet = false; bool fCriminals = false; @@ -2514,7 +2515,8 @@ bool CChainState::FlushStateToDisk( UnlinkPrunedFiles(setFilesToPrune); nLastWrite = nNow; } - static const size_t memoryCacheSizeMax = gArgs.GetBoolArg("-acindex", DEFAULT_ACINDEX) ? (nDefaultDbCache << 16) : (nDefaultDbCache << 10); + // use a bit more memory in normal usage + const size_t memoryCacheSizeMax = IsInitialBlockDownload() ? nCustomMemUsage : (nCustomMemUsage << 1); bool fMemoryCacheLarge = fDoFullFlush || (mode == FlushStateMode::IF_NEEDED && pcustomcsview->SizeEstimate() > memoryCacheSizeMax); // Flush best chain related state. This can only be done if the blocks / block index write was also done. if (fMemoryCacheLarge && !CoinsTip().GetBestBlock().IsNull()) { diff --git a/src/validation.h b/src/validation.h index bbc71446b1..d682fe15a2 100644 --- a/src/validation.h +++ b/src/validation.h @@ -158,6 +158,7 @@ extern bool fRequireStandard; extern bool fCheckBlockIndex; extern bool fCheckpointsEnabled; extern size_t nCoinCacheUsage; +extern size_t nCustomMemUsage; /** A fee rate smaller than this is considered zero fee (for relaying, mining and transaction creation) */ extern CFeeRate minRelayTxFee; /** If the tip is older than this (in seconds), the node is considered to be in initial block download. */ diff --git a/test/functional/rpc_getmininginfo.py b/test/functional/rpc_getmininginfo.py new file mode 100755 index 0000000000..40f1bdd22b --- /dev/null +++ b/test/functional/rpc_getmininginfo.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# Copyright (c) 2021 The DeFi Blockchain developers +# Distributed under the MIT software license, see the accompanying +# file LICENSE or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.test_framework import DefiTestFramework +from test_framework.util import ( + assert_equal, + connect_nodes_bi, +) + +class GetMiningInfoRPCTest(DefiTestFramework): + def set_test_params(self): + self.num_nodes = 2 + self.setup_clean_chain = True + + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + + def run_test(self): + node0_keys = self.nodes[0].get_genesis_keys() + node1_keys = self.nodes[1].get_genesis_keys() + + self.log.info("Import private keys...") + # node[0] has only one master node + self.nodes[0].importprivkey(node0_keys.operatorPrivKey) + + # node[1] has two master nodes + self.nodes[1].importprivkey(node0_keys.operatorPrivKey) + self.nodes[1].importprivkey(node1_keys.operatorPrivKey) + + operators = [node0_keys.operatorAuthAddress, node1_keys.operatorAuthAddress] + + self.log.info("Restart nodes...") + self.restart_node(0, ['-gen', '-masternode_operator=' + operators[0]]) + self.restart_node(1, ['-gen', '-rewardaddress=' + operators[1]] + + ['-masternode_operator=' + x for x in operators]) + + connect_nodes_bi(self.nodes, 0, 1) + + self.log.info("Mining blocks ...") + self.nodes[0].generate(10) + self.sync_all() + self.nodes[1].generate(10) + self.sync_all() + + # getmininginfo() on node[0], should only return one master node in the response array + resp0 = self.nodes[0].getmininginfo() + assert_equal(len(resp0['masternodes']), 1) + + # getmininginfo() on node[1], should return two master nodes in the response array + resp1 = self.nodes[1].getmininginfo() + assert_equal(len(resp1['masternodes']), 2) + +if __name__ == '__main__': + GetMiningInfoRPCTest().main() diff --git a/test/functional/rpc_listaccounthistory.py b/test/functional/rpc_listaccounthistory.py index 5434aaf8e9..8d98640a99 100755 --- a/test/functional/rpc_listaccounthistory.py +++ b/test/functional/rpc_listaccounthistory.py @@ -71,5 +71,11 @@ def run_test(self): assert_equal(results[0]['owner'], collateral_a) assert_equal(results[0]['type'], 'MintToken') + result = self.nodes[0].listaccounthistory() + assert 'blockReward' in [res['type'] for res in result] + + result = self.nodes[1].listaccounthistory() + assert_equal(result, []) + if __name__ == '__main__': TokensRPCListAccountHistory().main () diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index a0722b45d1..942433d46d 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -245,6 +245,7 @@ 'feature_help.py', 'feature_shutdown.py', 'feature_oracles.py', + 'rpc_getmininginfo.py', # Don't append tests at the end to avoid merge conflicts # Put them in a random line within the section that fits their approximate run-time ]