diff --git a/doc/release-notes.md b/doc/release-notes.md index a3ef7c8c25bb..25a43605c1f7 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -88,6 +88,7 @@ RPC Changes - "CoinStake" JSON object in `getblock` output is removed, and replaced with the strings "stakeModifier" and "hashProofOfStake" + - "isPublicSpend" boolean (optional) input parameter is removed from the following commands: - `createrawzerocoinspend` - `spendzerocoin` @@ -96,16 +97,39 @@ RPC Changes These commands are now able to create only *public* spends (private spends were already enabled only on regtest). + - "mintchange" and "minimizechange" boolean input parameters are removed from the following commands: - `spendzerocoin` Mints are disabled, therefore it is no longer possible to mint the change of a zerocoin spend. The change is minimized by default. + - `setstakesplitthreshold` now accepts decimal amounts. If the provided value is `0`, split staking gets disabled. `getstakesplitthreshold` returns a double. - `dumpwallet` no longer allows overwriting files. This is a security measure as well as prevents dangerous user mistakes. +- The output of `getstakingstatus` was reworked. It now shows the following information: + ``` + { + "staking_status": true|false, (boolean) whether the wallet is staking or not + "staking_enabled": true|false, (boolean) whether staking is enabled/disabled in pivx.conf + "coldstaking_enabled": true|false, (boolean) whether cold-staking is enabled/disabled in pivx.conf + "haveconnections": true|false, (boolean) whether network connections are present + "mnsync": true|false, (boolean) whether masternode data is synced + "walletunlocked": true|false, (boolean) whether the wallet is unlocked + "stakeablecoins": n, (numeric) number of stakeable UTXOs + "stakingbalance": d, (numeric) PIV value of the stakeable coins (minus reserve balance, if any) + "stakesplitthreshold": d, (numeric) value of the current threshold for stake split + "lastattempt_age": n, (numeric) seconds since last stake attempt + "lastattempt_depth": n, (numeric) depth of the block on top of which the last stake attempt was made + "lastattempt_hash": xxx, (hex string) hash of the block on top of which the last stake attempt was made + "lastattempt_coins": n, (numeric) number of stakeable coins available during last stake attempt + "lastattempt_tries": n, (numeric) number of stakeable coins checked during last stake attempt + } + ``` + + ### Removed commands The following commands have been removed from the RPC interface: diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index dea4e6495214..b32111c721b5 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -592,16 +592,20 @@ UniValue getstakingstatus(const UniValue& params, bool fHelp) "\nResult:\n" "{\n" - " \"staking_status\": true|false, (boolean) if the wallet is staking or not\n" - " \"staking_enabled\": true|false, (boolean) if staking is enabled/disabled in pivx.conf\n" - " \"tiptime\": n, (integer) chain tip blocktime\n" - " \"haveconnections\": true|false, (boolean) if network connections are present\n" - " \"mnsync\": true|false, (boolean) if masternode data is synced\n" - " \"walletunlocked\": true|false, (boolean) if the wallet is unlocked\n" - " \"stakeablecoins\": true|false, (boolean) if the wallet has mintable balance (greater than reserve balance)\n" - " \"hashLastStakeAttempt\": xxx (hex string) hash of last block on top of which the miner attempted to stake\n" - " \"heightLastStakeAttempt\": n (integer) height of last block on top of which the miner attempted to stake\n" - " \"timeLastStakeAttempt\": n (integer) time of last attempted stake\n" + " \"staking_status\": true|false, (boolean) whether the wallet is staking or not\n" + " \"staking_enabled\": true|false, (boolean) whether staking is enabled/disabled in pivx.conf\n" + " \"coldstaking_enabled\": true|false, (boolean) whether cold-staking is enabled/disabled in pivx.conf\n" + " \"haveconnections\": true|false, (boolean) whether network connections are present\n" + " \"mnsync\": true|false, (boolean) whether the required masternode/spork data is synced\n" + " \"walletunlocked\": true|false, (boolean) whether the wallet is unlocked\n" + " \"stakeablecoins\": n (numeric) number of stakeable UTXOs\n" + " \"stakingbalance\": d (numeric) PIV value of the stakeable coins (minus reserve balance, if any)\n" + " \"stakesplitthreshold\": d (numeric) value of the current threshold for stake split\n" + " \"lastattempt_age\": n (numeric) seconds since last stake attempt\n" + " \"lastattempt_depth\": n (numeric) depth of the block on top of which the last stake attempt was made\n" + " \"lastattempt_hash\": xxx (hex string) hash of the block on top of which the last stake attempt was made\n" + " \"lastattempt_coins\": n (numeric) number of stakeable coins available during last stake attempt\n" + " \"lastattempt_tries\": n (numeric) number of stakeable coins checked during last stake attempt\n" "}\n" "\nExamples:\n" + @@ -615,16 +619,24 @@ UniValue getstakingstatus(const UniValue& params, bool fHelp) UniValue obj(UniValue::VOBJ); obj.push_back(Pair("staking_status", pwalletMain->pStakerStatus->IsActive())); obj.push_back(Pair("staking_enabled", GetBoolArg("-staking", true))); - obj.push_back(Pair("tiptime", (int)chainActive.Tip()->nTime)); + bool fColdStaking = GetBoolArg("-coldstaking", true); + obj.push_back(Pair("coldstaking_enabled", fColdStaking)); obj.push_back(Pair("haveconnections", !vNodes.empty())); - obj.push_back(Pair("mnsync", masternodeSync.IsSynced())); + obj.push_back(Pair("mnsync", !masternodeSync.NotCompleted())); obj.push_back(Pair("walletunlocked", !pwalletMain->IsLocked())); - obj.push_back(Pair("stakeablecoins", pwalletMain->StakeableCoins())); - uint256 lastHash = pwalletMain->pStakerStatus->GetLastHash(); - obj.push_back(Pair("hashLastStakeAttempt", lastHash.GetHex())); - obj.push_back(Pair("heightLastStakeAttempt", (mapBlockIndex.count(lastHash) > 0 ? - mapBlockIndex.at(lastHash)->nHeight : -1)) ); - obj.push_back(Pair("timeLastStakeAttempt", pwalletMain->pStakerStatus->GetLastTime())); + std::vector vCoins; + pwalletMain->StakeableCoins(&vCoins); + obj.push_back(Pair("stakeablecoins", (int)vCoins.size())); + obj.push_back(Pair("stakingbalance", ValueFromAmount(pwalletMain->GetStakingBalance(fColdStaking)))); + obj.push_back(Pair("stakesplitthreshold", ValueFromAmount(pwalletMain->nStakeSplitThreshold))); + CStakerStatus* ss = pwalletMain->pStakerStatus; + if (ss) { + obj.push_back(Pair("lastattempt_age", (int)(GetTime() - ss->GetLastTime()))); + obj.push_back(Pair("lastattempt_depth", (chainActive.Height() - ss->GetLastHeight()))); + obj.push_back(Pair("lastattempt_hash", ss->GetLastHash().GetHex())); + obj.push_back(Pair("lastattempt_coins", ss->GetLastCoins())); + obj.push_back(Pair("lastattempt_tries", ss->GetLastTries())); + } return obj; } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index b8a3c7abe996..704a518e5de8 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2549,6 +2549,7 @@ bool CWallet::CreateCoinStake( // update staker status (hash) pStakerStatus->SetLastTip(pindexPrev); + pStakerStatus->SetLastCoins(listInputs.size()); // Kernel Search CAmount nCredit; @@ -2573,8 +2574,9 @@ bool CWallet::CreateCoinStake( nAttempts++; fKernelFound = Stake(pindexPrev, stakeInput.get(), nBits, nTxNewTime, hashProofOfStake); - // update staker status (time) + // update staker status (time, attempts) pStakerStatus->SetLastTime(nTxNewTime); + pStakerStatus->SetLastTries(nAttempts); if (!fKernelFound) continue; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index e8234bf558e9..7145dd21672b 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -117,7 +117,7 @@ enum ZerocoinSpendStatus { ZPIV_INVALID_WITNESS = 12, // Spend coin transaction did not verify ZPIV_BAD_SERIALIZATION = 13, // Transaction verification failed ZPIV_SPENT_USED_ZPIV = 14, // Coin has already been spend - ZPIV_TX_TOO_LARGE = 15, // The transaction is larger than the max tx size + ZPIV_TX_TOO_LARGE = 15, // The transaction is larger than the max tx size ZPIV_SPEND_V1_SEC_LEVEL // Spend is V1 and security level is not set to 100 }; @@ -177,26 +177,42 @@ class CKeyPool } }; -/** Record info about last kernel stake operation (time and chainTip)**/ -class CStakerStatus { +/** Record info about last stake attempt: + * - tipBlock index of the block on top of which last stake attempt was made + * - nTime time slot of last attempt + * - nTries number of UTXOs hashed during last attempt + * - nCoins number of stakeable utxos during last attempt +**/ +class CStakerStatus +{ private: - const CBlockIndex* tipLastStakeAttempt = nullptr; - int64_t timeLastStakeAttempt; + const CBlockIndex* tipBlock{nullptr}; + int64_t nTime{0}; + int nTries{0}; + int nCoins{0}; + public: - const CBlockIndex* GetLastTip() const { return tipLastStakeAttempt; } - uint256 GetLastHash() const - { - return (tipLastStakeAttempt == nullptr ? UINT256_ZERO : tipLastStakeAttempt->GetBlockHash()); - } - int64_t GetLastTime() const { return timeLastStakeAttempt; } - void SetLastTip(const CBlockIndex* lastTip) { tipLastStakeAttempt = lastTip; } - void SetLastTime(const uint64_t lastTime) { timeLastStakeAttempt = lastTime; } + // Get + const CBlockIndex* GetLastTip() const { return tipBlock; } + uint256 GetLastHash() const { return (GetLastTip() == nullptr ? UINT256_ZERO : GetLastTip()->GetBlockHash()); } + int GetLastHeight() const { return (GetLastTip() == nullptr ? 0 : GetLastTip()->nHeight); } + int GetLastCoins() const { return nCoins; } + int GetLastTries() const { return nTries; } + int64_t GetLastTime() const { return nTime; } + // Set + void SetLastCoins(const int coins) { nCoins = coins; } + void SetLastTries(const int tries) { nTries = tries; } + void SetLastTip(const CBlockIndex* lastTip) { tipBlock = lastTip; } + void SetLastTime(const uint64_t lastTime) { nTime = lastTime; } void SetNull() { + SetLastCoins(0); + SetLastTries(0); SetLastTip(nullptr); SetLastTime(0); } - bool IsActive() { return (timeLastStakeAttempt + 30) >= GetTime(); } + // Check whether staking status is active (last attempt earlier than 30 seconds ago) + bool IsActive() const { return (nTime + 30) >= GetTime(); } }; /** diff --git a/test/functional/mining_pos_coldStaking.py b/test/functional/mining_pos_coldStaking.py index 578737ea2b2b..facda3db93de 100755 --- a/test/functional/mining_pos_coldStaking.py +++ b/test/functional/mining_pos_coldStaking.py @@ -112,7 +112,7 @@ def run_test(self): assert (self.nodes[1].lockunspent(False, [{"txid": x['txid'], "vout": x['vout']}])) # check that it cannot stake sleep(1) - assert_equal(self.nodes[1].getstakingstatus()["stakeablecoins"], False) + assert_equal(self.nodes[1].getstakingstatus()["stakeablecoins"], 0) # 3) nodes[0] generates a owner address # nodes[1] generates a cold-staking address. @@ -202,7 +202,7 @@ def run_test(self): # ----------------------------------------------------------- print("*** 7 ***") self.log.info("Trying to generate a cold-stake block before whitelisting the owner...") - assert_equal(self.nodes[1].getstakingstatus()["stakeablecoins"], False) + assert_equal(self.nodes[1].getstakingstatus()["stakeablecoins"], 0) self.log.info("Nice. Cold staker was NOT able to create the block yet.") self.log.info("Whitelisting the owner...") @@ -226,7 +226,7 @@ def run_test(self): # 9) check that the staker can use the coins to stake a block with internal miner. # -------------------------------------------------------------------------------- print("*** 9 ***") - assert_equal(self.nodes[1].getstakingstatus()["stakeablecoins"], True) + assert_equal(self.nodes[1].getstakingstatus()["stakeablecoins"], NUM_OF_INPUTS-1) self.log.info("Generating one valid cold-stake block...") self.mocktime = self.generate_pos(1, self.mocktime) self.log.info("New block created by cold-staking. Trying to submit...") @@ -357,7 +357,7 @@ def run_test(self): # ----------------------------------------------------------- print("*** 14 ***") self.log.info("Trying to generate one cold-stake block again...") - assert_equal(self.nodes[1].getstakingstatus()["stakeablecoins"], False) + assert_equal(self.nodes[1].getstakingstatus()["stakeablecoins"], 0) self.log.info("Cigar. Cold staker was NOT able to create any more blocks.") # 15) check balances when mature. diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 2545de85ef1b..acf8c06e8ff7 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -1009,7 +1009,8 @@ def generate_pos(self, node_id, btime=None): rpc_conn = self.nodes[node_id] ss = rpc_conn.getstakingstatus() assert ss["walletunlocked"] - assert ss["stakeablecoins"] + assert ss["stakeablecoins"] > 0 + assert ss["stakingbalance"] > 0.0 if btime is not None: next_btime = btime + 60 fStaked = False @@ -1024,7 +1025,7 @@ def generate_pos(self, node_id, btime=None): # couldn't generate block. check that this node can still stake (after 60 failures) if failures > 60: ss = rpc_conn.getstakingstatus() - if not (ss["walletunlocked"] and ss["stakeablecoins"]): + if not (ss["walletunlocked"] and ss["stakeablecoins"] > 0 and ss["stakingbalance"] > 0.0): raise AssertionError("Node %d unable to stake!" % node_id) # try to stake one sec in the future if btime is not None: