diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 60857b502..3c71b636e 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -190,7 +190,8 @@ 'sendtoaddress.py', 'stakeimmaturebalance.py', 'rpc-help.py', - 'createrawscriptaddress.py' + 'createrawscriptaddress.py', + 'cfunddb_statehash.py' ] #if ENABLE_ZMQ: # testScripts.append('zmq_test.py') diff --git a/qa/rpc-tests/cfunddb-statehash.py b/qa/rpc-tests/cfunddb-statehash.py new file mode 100644 index 000000000..fe159ab99 --- /dev/null +++ b/qa/rpc-tests/cfunddb-statehash.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +# Copyright (c) 2019 The NavCoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +from test_framework.test_framework import NavCoinTestFramework +from test_framework.util import * +import urllib.parse + +class CFundDBStateHash(NavCoinTestFramework): + def __init__(self): + super().__init__() + self.num_nodes = 2 + self.setup_clean_chain = True + + def setup_network(self): + self.nodes = [] + self.nodes.append(start_node(0, self.options.tmpdir, ["-headerspamfilter=0"])) + self.nodes.append(start_node(1, self.options.tmpdir, ["-headerspamfilter=0"])) + connect_nodes(self.nodes[0], 1) + self.is_network_split = False + + def run_test(self): + self.nodes[1].generate(300) + self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(),100000) + hash="" + + for i in range(20): + phash=self.nodes[1].createproposal(self.nodes[1].getnewaddress(), 100, 1000, "test")["hash"] + if (hash == ""): + hash = phash + self.nodes[1].generate(1) + sync_blocks(self.nodes) + self.nodes[1].proposalvote(phash, 'yes') + + self.nodes[1].invalidateblock(self.nodes[1].getblockhash(311)) + self.nodes[1].donatefund(100000) + self.nodes[1].generate(30) + sync_blocks(self.nodes) + + assert_equal(self.nodes[0].getcfunddbstatehash(), self.nodes[1].getcfunddbstatehash()) + + self.nodes[1].generate(60) + sync_blocks(self.nodes) + + assert_equal(self.nodes[1].getinfo()['errors'],"") + + raw_preq = self.nodes[1].createpaymentrequest(hash, 100, "preq", True) + + # Disconnect Nodes 0 and 1 + url = urllib.parse.urlparse(self.nodes[1].url) + self.nodes[0].disconnectnode(url.hostname+":"+str(p2p_port(1))) + + self.nodes[0].sendrawtransaction(raw_preq) + self.nodes[1].sendrawtransaction(raw_preq) + + self.nodes[0].generate(1) + self.nodes[1].generate(5) + + assert(self.nodes[0].getbestblockhash() != self.nodes[1].getbestblockhash()) + + connect_nodes(self.nodes[0], 1) + + self.sync_all() + + assert_equal(self.nodes[0].getbestblockhash(), self.nodes[1].getbestblockhash()) + + +if __name__ == '__main__': + CFundDBStateHash().main() diff --git a/qa/rpc-tests/getstakereport.py b/qa/rpc-tests/getstakereport.py index aa5c4e1b2..6cf960f01 100755 --- a/qa/rpc-tests/getstakereport.py +++ b/qa/rpc-tests/getstakereport.py @@ -125,7 +125,7 @@ def run_test(self): assert_equal('6.00', spending_address_last_7d) assert_equal('4.00', staking_address_last_7d) - # Load the averages for stake amounts + # Load the averages for stake amounts avg_last7d = self.nodes[0].getstakereport()['Last 7 Days Avg'] avg_last30d = self.nodes[0].getstakereport()['Last 30 Days Avg'] avg_last365d = self.nodes[0].getstakereport()['Last 365 Days Avg'] @@ -161,7 +161,7 @@ def run_test(self): assert_equal('2.00', spending_address_last_7d) assert_equal('2.00', staking_address_last_7d) - # Load the averages for stake amounts + # Load the averages for stake amounts avg_last7d = self.nodes[0].getstakereport()['Last 7 Days Avg'] avg_last30d = self.nodes[0].getstakereport()['Last 30 Days Avg'] avg_last365d = self.nodes[0].getstakereport()['Last 365 Days Avg'] @@ -197,7 +197,7 @@ def run_test(self): assert_equal('0.00', spending_address_last_7d) assert_equal('0.00', staking_address_last_7d) - # Load the averages for stake amounts + # Load the averages for stake amounts avg_last7d = self.nodes[0].getstakereport()['Last 7 Days Avg'] avg_last30d = self.nodes[0].getstakereport()['Last 30 Days Avg'] avg_last365d = self.nodes[0].getstakereport()['Last 365 Days Avg'] @@ -207,7 +207,34 @@ def run_test(self): assert_equal('0.06666666', avg_last30d) assert_equal('0.19354838', avg_last365d) - def stake_block(self, node): + # Disconnect the nodes + for node in self.nodes[0].getpeerinfo(): + self.nodes[0].disconnectnode(node['addr']) + time.sleep(2) #disconnecting a node needs a little bit of time + assert(self.nodes[0].getpeerinfo() == []) + + # Stake a block on node 0 + orphaned_block_hash = self.stake_block(self.nodes[0], False) + + # Generate some blocks on node 1 + self.nodes[1].generate(100) + + # Reconnect the nodes + connect_nodes(self.nodes[0], 1) + connect_nodes(self.nodes[1], 2) + connect_nodes(self.nodes[2], 0) + + # Wait for blocks to sync + self.sync_all() + + # Make sure the block was orphaned + assert(self.nodes[0].getblock(orphaned_block_hash)['confirmations'] == -1) + + # Check the staked amount + # Should be 0 (Zero) as the last staked block is orphaned + assert_equal('0.00', self.nodes[0].getstakereport()['Last 7 Days']) + + def stake_block(self, node, mature = True): # Get the current block count to check against while we wait for a stake blockcount = node.getblockcount() @@ -225,9 +252,17 @@ def stake_block(self, node): # Turn staking off node.staking(False) - # Make sure the blocks are mature before we check the report - slow_gen(node, 5, 0.5) - self.sync_all() + # Get the staked block + block_hash = node.getbestblockhash() + + # Only mature the blocks if we asked for it + if (mature): + # Make sure the blocks are mature before we check the report + slow_gen(node, 5, 0.5) + self.sync_all() + + # return the block hash to the function caller + return block_hash if __name__ == '__main__': diff --git a/qa/rpc-tests/stakeimmaturebalance.py b/qa/rpc-tests/stakeimmaturebalance.py index 74e9eba38..f398b1ade 100755 --- a/qa/rpc-tests/stakeimmaturebalance.py +++ b/qa/rpc-tests/stakeimmaturebalance.py @@ -29,10 +29,10 @@ def setup_network(self, split=False): def run_test(self): addr = self.nodes[1].getnewaddress() activate_staticr(self.nodes[0]) - self.nodes[0].sendtoaddress(addr, satoshi_round(float(self.nodes[0].getbalance()) - SENDING_FEE)) + self.nodes[0].sendtoaddress(addr, satoshi_round(self.nodes[0].getbalance()), "", "", "", True) slow_gen(self.nodes[0], 1) logging.info('Checking stake weight') - assert(self.nodes[0].getbalance() - BLOCK_REWARD < 1), 'Wallet balance not sent' + assert(self.nodes[0].getbalance() - BLOCK_REWARD == 0), 'Wallet balance not sent' assert(self.nodes[0].getwalletinfo()['immature_balance'] > 0), 'No immature balance' assert(self.nodes[0].getstakinginfo()['weight'] == 0), 'Immature balance affecting staking weight' diff --git a/qa/rpc-tests/wallet.py b/qa/rpc-tests/wallet.py index 3fb086f4e..04cf8aba6 100755 --- a/qa/rpc-tests/wallet.py +++ b/qa/rpc-tests/wallet.py @@ -231,25 +231,27 @@ def run_test(self): txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted) assert_equal(self.nodes[2].getbalance(), node_2_bal) - #create another tx txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2) + block_count = self.nodes[0].getblockcount() + #restart the nodes with -walletbroadcast=1 stop_nodes(self.nodes) wait_navcoinds() - self.nodes = start_nodes(3, self.options.tmpdir) + self.nodes = start_nodes(3, self.options.tmpdir, [['-staking=0'], ['-staking=0'], ['-staking=0']]) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,2,0) + slow_gen(self.nodes[0], 2) self.sync_all() - slow_gen(self.nodes[0], 1) - self.sync_all() + + block_count_new = self.nodes[0].getblockcount() # We need to adjust the balance since new block/s got confirmed # And we sent 2 NAV to it - node_2_bal += 2 * BLOCK_REWARD + 2 + node_2_bal += (block_count_new - block_count) * BLOCK_REWARD + 2 #tx should be added to balance because after restarting the nodes tx should be broadcastet assert_equal(self.nodes[2].getbalance(), node_2_bal) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 3ea29c68a..7a7933bd2 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -228,8 +228,8 @@ class CMainParams : public CChainParams { base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x88)(0xB2)(0x1E).convert_to_container >(); base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x88)(0xAD)(0xE4).convert_to_container >(); - vSeeds.push_back(CDNSSeedData("nav.community", "seed.nav.community")); - vSeeds.push_back(CDNSSeedData("navcoin.org", "seed.navcoin.org")); + vSeeds.push_back(CDNSSeedData("seed 1 nav.community", "seed.nav.community")); + vSeeds.push_back(CDNSSeedData("seed 2 nav.community", "seed2.nav.community")); vFixedSeeds = std::vector(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main)); diff --git a/src/coins.cpp b/src/coins.cpp index 313c5c33c..7b31e748c 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -6,6 +6,8 @@ #include #include +#include +#include #include @@ -442,6 +444,41 @@ void CCoinsViewCache::Uncache(const uint256& hash) } } +uint256 CCoinsViewCache::GetCFundDBStateHash() +{ + CPaymentRequestMap mapPaymentRequests; + CProposalMap mapProposals; + + int64_t nTimeStart = GetTimeMicros(); + + if (GetAllProposals(mapProposals) && GetAllPaymentRequests(mapPaymentRequests)) + { + CHashWriter writer(0,0); + + for (auto &it: mapProposals) + { + if (!it.second.IsNull()) + writer << it.second; + } + + for (auto &it: mapPaymentRequests) + { + if (!it.second.IsNull()) + writer << it.second; + } + + uint256 ret = writer.GetHash(); + int64_t nTimeEnd = GetTimeMicros(); + + LogPrint("bench", " Benchmark: Calculate CFundDB state hash: %.2fms\n", (nTimeEnd - nTimeStart) * 0.001); + + return ret; + + } + + return uint256(); +} + unsigned int CCoinsViewCache::GetCacheSize() const { return cacheCoins.size(); } diff --git a/src/coins.h b/src/coins.h index 830570763..e7bb3662e 100644 --- a/src/coins.h +++ b/src/coins.h @@ -310,7 +310,7 @@ struct CCacheEntry typedef CCacheEntry CCoinsCacheEntry; typedef boost::unordered_map CCoinsMap; typedef std::map CProposalMap; -typedef boost::unordered_map CPaymentRequestMap; +typedef std::map CPaymentRequestMap; /** Cursor for iterating over CoinsView state */ class CCoinsViewCursor @@ -483,6 +483,8 @@ class CCoinsViewCache : public CCoinsViewBacked bool RemoveProposal(const uint256 &pid) const; bool RemovePaymentRequest(const uint256 &prid) const; + uint256 GetCFundDBStateHash(); + /** * Check if we have the given tx already loaded in this cache. * The semantics are the same as HaveCoins(), but no calls to diff --git a/src/main.cpp b/src/main.cpp index 180c9cb1b..ea5b2ae57 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5385,6 +5385,8 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, nCheckLevel = std::max(0, std::min(4, nCheckLevel)); LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); CCoinsViewCache coins(coinsview); + uint256 prevStateHash; + if (nCheckLevel >= 4) prevStateHash = coins.GetCFundDBStateHash(); CBlockIndex* pindexState = chainActive.Tip(); CBlockIndex* pindexFailure = nullptr; int nGoodTransactions = 0; @@ -5458,6 +5460,9 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); CFundStep(state, pindex, false, coins); } + uint256 nowStateHash = coins.GetCFundDBStateHash(); + if (prevStateHash != nowStateHash) + return error("VerifyDB(): *** the cfund db state hash differs after reconnecting blocks. it was %d, it is %s after\n", prevStateHash.ToString(), nowStateHash.ToString()); } LogPrintf("[DONE].\n"); diff --git a/src/main.h b/src/main.h index 21e6715b7..1d5561a16 100644 --- a/src/main.h +++ b/src/main.h @@ -96,7 +96,7 @@ static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB /** Maximum number of script-checking threads allowed */ static const int MAX_SCRIPTCHECK_THREADS = 16; /** -par default (number of script-checking threads, 0 = auto) */ -static const int DEFAULT_SCRIPTCHECK_THREADS = 2; +static const int DEFAULT_SCRIPTCHECK_THREADS = 0; /** Number of blocks that can be requested at any given time from a single peer. */ static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16; /** Timeout in seconds during which a peer must stall block download progress before being disconnected. */ @@ -223,10 +223,10 @@ extern bool fPruneMode; /** Number of MiB of block files that we're trying to stay below. */ extern uint64_t nPruneTarget; /** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of chainActive.Tip() will not be pruned. */ -static const unsigned int MIN_BLOCKS_TO_KEEP = 288; +static const unsigned int MIN_BLOCKS_TO_KEEP = 2880; static const signed int DEFAULT_CHECKBLOCKS = MIN_BLOCKS_TO_KEEP; -static const unsigned int DEFAULT_CHECKLEVEL = 3; +static const unsigned int DEFAULT_CHECKLEVEL = 4; // Require that user allocate at least 550MB for block & undo files (blk???.dat and rev???.dat) // At 1MB per block, 288 blocks = 288MB. diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 1bbafd332..b42845e7d 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1087,6 +1087,25 @@ UniValue cfundstats(const UniValue& params, bool fHelp) } +UniValue getcfunddbstatehash(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getcfunddbstatehash\n" + "\nReturns the hash of the Cfund DB current state.\n" + "\nResult\n" + "\"hex\" (string) the hash hex encoded\n" + "\nExamples\n" + + HelpExampleCli("getcfunddbstatehash", "") + + HelpExampleRpc("getcfunddbstatehash", "") + ); + + LOCK(cs_main); + + CCoinsViewCache view(pcoinsTip); + return view.GetCFundDBStateHash().ToString(); +} + UniValue gettxout(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 2 || params.size() > 3) @@ -1645,6 +1664,7 @@ static const CRPCCommand commands[] = { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true }, { "blockchain", "verifychain", &verifychain, true }, { "communityfund", "listproposals", &listproposals, true }, + { "communityfund", "getcfunddbstatehash", &getcfunddbstatehash, true }, /* Not shown in help */ { "hidden", "invalidateblock", &invalidateblock, true }, diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index b3ecc5e17..0ed35856d 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3187,7 +3187,7 @@ bool IsTxCountedAsStaked(const CWalletTx* tx) LOCK(cs_main); // orphan block or immature - if ((!tx->GetDepthInMainChain()) || (tx->GetBlocksToMaturity() > 0)) + if ((!tx->GetDepthInMainChain()) || (tx->GetBlocksToMaturity() > 0) || !tx->IsInMainChain()) return false; // abandoned transactions