Skip to content

Commit

Permalink
Merged in stake-timing-adjustments (pull request #28)
Browse files Browse the repository at this point in the history
Stake timing adjustments

Approved-by: Cevap
  • Loading branch information
FornaxA authored and Cevap committed Mar 9, 2020
2 parents c233181 + 56fad15 commit 01f32a5
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 82 deletions.
46 changes: 0 additions & 46 deletions src/pos/kernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,52 +331,6 @@ bool HasStakeMinAgeOrDepth(const int contextHeight, const uint32_t contextTime,
return (contextHeight - utxoFromBlockHeight >= Params().GetConsensus().nStakeMinDepth);
}

bool Stake(const CBlockIndex* pindexPrev, CStakeInput* stakeInput, unsigned int nBits, unsigned int& nTimeTx, uint256& hashProofOfStake)
{
int prevHeight = pindexPrev->nHeight;

// get stake input pindex
CBlockIndex* pindexFrom = stakeInput->GetIndexFrom();
if (!pindexFrom || pindexFrom->nHeight < 1) return error("%s : no pindexfrom", __func__);

const uint32_t nTimeBlockFrom = pindexFrom->nTime;
const int nHeightBlockFrom = pindexFrom->nHeight;

// check for maturity (min age/depth) requirements
if (!HasStakeMinAgeOrDepth(prevHeight + 1, nTimeTx, nHeightBlockFrom, nTimeBlockFrom))
return error("%s : min age violation - height=%d - nTimeTx=%d, nTimeBlockFrom=%d, nHeightBlockFrom=%d",
__func__, prevHeight + 1, nTimeTx, nTimeBlockFrom, nHeightBlockFrom);

// iterate the hashing
bool fSuccess = false;
const unsigned int nHashDrift = 60;
const unsigned int nFutureTimeDriftPoS = 180;
unsigned int nTryTime = nTimeTx - 1;
// iterate from nTimeTx up to nTimeTx + nHashDrift
// but not after the max allowed future blocktime drift (3 minutes for PoS)
const unsigned int maxTime = std::min(nTimeTx + nHashDrift, (uint32_t)GetAdjustedTime() + nFutureTimeDriftPoS);

while (nTryTime < maxTime)
{
//new block came in, move on
if (chainActive.Height() != prevHeight)
break;

++nTryTime;

// if stake hash does not meet the target then continue to next iteration
if (!CheckStakeKernelHash(pindexPrev, nBits, stakeInput, nTryTime, hashProofOfStake))
continue;

// if we made it this far, then we have successfully found a valid kernel hash
fSuccess = true;
nTimeTx = nTryTime;
break;
}

return fSuccess;
}

bool ContextualCheckZerocoinStake(int nPreviousBlockHeight, CStakeInput* stake)
{
if (nPreviousBlockHeight < Params().GetConsensus().nBlockZerocoinV2)
Expand Down
1 change: 0 additions & 1 deletion src/pos/kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ static const int MODIFIER_INTERVAL_RATIO = 3;
bool GetKernelStakeModifier(uint256 hashBlockFrom, uint64_t& nStakeModifier, int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake);
bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64_t& nStakeModifier, bool& fGeneratedStakeModifier);
bool ComputeStakeModifierV2(CBlockIndex* pindex, const uint256& kernel);
bool Stake(const CBlockIndex* pindexPrev, CStakeInput* stakeInput, unsigned int nBits, unsigned int& nTimeTx, uint256& hashProofOfStake);

// Initialize the stake input object
bool initStakeInput(const CBlock block, std::unique_ptr<CStakeInput>& stake, int nPreviousBlockHeight);
Expand Down
119 changes: 100 additions & 19 deletions src/pos/staking-manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "staking-manager.h"

#include "init.h"
#include "masternode/masternode-sync.h"
#include "miner.h"
#include "net.h"
#include "policy/policy.h"
Expand All @@ -21,8 +22,9 @@
std::shared_ptr<CStakingManager> stakingManager;

CStakingManager::CStakingManager(CWallet * const pwalletIn) :
nMintableLastCheck(0), fMintableCoins(false), nExtraNonce(0), // Currently unused
fEnableStaking(false), fEnableIONStaking(false), nReserveBalance(0), pwallet(pwalletIn) {}
nMintableLastCheck(0), fMintableCoins(false), fLastLoopOrphan(false), nExtraNonce(0), // Currently unused
fEnableStaking(false), fEnableIONStaking(false), nReserveBalance(0), pwallet(pwalletIn),
nHashInterval(22), nLastCoinStakeSearchInterval(0), nLastCoinStakeSearchTime(GetAdjustedTime()) {}

bool CStakingManager::MintableCoins()
{
Expand Down Expand Up @@ -87,6 +89,54 @@ bool CStakingManager::SelectStakeCoins(std::list<std::unique_ptr<CStakeInput> >&
return true;
}

bool CStakingManager::Stake(const CBlockIndex* pindexPrev, CStakeInput* stakeInput, unsigned int nBits, unsigned int& nTimeTx, uint256& hashProofOfStake)
{
int prevHeight = pindexPrev->nHeight;

// get stake input pindex
CBlockIndex* pindexFrom = stakeInput->GetIndexFrom();
if (!pindexFrom || pindexFrom->nHeight < 1) return error("%s : no pindexfrom", __func__);

const uint32_t nTimeBlockFrom = pindexFrom->nTime;
const int nHeightBlockFrom = pindexFrom->nHeight;

// check for maturity (min age/depth) requirements
if (!HasStakeMinAgeOrDepth(prevHeight + 1, nTimeTx, nHeightBlockFrom, nTimeBlockFrom))
return error("%s : min age violation - height=%d - nTimeTx=%d, nTimeBlockFrom=%d, nHeightBlockFrom=%d",
__func__, prevHeight + 1, nTimeTx, nTimeBlockFrom, nHeightBlockFrom);

// iterate the hashing
bool fSuccess = false;
const unsigned int nHashDrift = 60;
const unsigned int nFutureTimeDriftPoS = 180;
unsigned int nTryTime = nTimeTx - 1;
// iterate from nTimeTx up to nTimeTx + nHashDrift
// but not after the max allowed future blocktime drift (3 minutes for PoS)
const unsigned int maxTime = std::min(nTimeTx + nHashDrift, (uint32_t)GetAdjustedTime() + nFutureTimeDriftPoS);

while (nTryTime < maxTime)
{
//new block came in, move on
if (chainActive.Height() != prevHeight)
break;

++nTryTime;

// if stake hash does not meet the target then continue to next iteration
if (!CheckStakeKernelHash(pindexPrev, nBits, stakeInput, nTryTime, hashProofOfStake))
continue;

// if we made it this far, then we have successfully found a valid kernel hash
fSuccess = true;
nTimeTx = nTryTime;
break;
}

mapHashedBlocks.clear();
mapHashedBlocks[chainActive.Tip()->nHeight] = GetTime(); //store a time stamp of when we last hashed on this block
return fSuccess;
}

bool CStakingManager::CreateCoinStake(const CBlockIndex* pindexPrev, std::shared_ptr<CMutableTransaction>& coinstakeTx, std::shared_ptr<CStakeInput>& coinstakeInput) {
// Needs wallet
if (pwallet == nullptr || pindexPrev == nullptr)
Expand All @@ -113,12 +163,6 @@ bool CStakingManager::CreateCoinStake(const CBlockIndex* pindexPrev, std::shared
return false;
}

if (listInputs.empty()) {
LogPrint(BCLog::STAKING, "CreateCoinStake(): listInputs empty\n");
// MilliSleep(50000);
return false;
}

if (GetAdjustedTime() - chainActive.Tip()->GetBlockTime() < 60) {
if (Params().NetworkIDString() == CBaseChainParams::REGTEST) {
// MilliSleep(1000);
Expand Down Expand Up @@ -190,6 +234,14 @@ bool CStakingManager::CreateCoinStake(const CBlockIndex* pindexPrev, std::shared
return true;
}

bool CStakingManager::IsStaking() {
bool nStaking = false;
if (mapHashedBlocks.count(chainActive.Tip()->nHeight))
nStaking = true;
else if (mapHashedBlocks.count(chainActive.Tip()->nHeight - 1) && nLastCoinStakeSearchInterval)
nStaking = true;
}

void CStakingManager::UpdatedBlockTip(const CBlockIndex* pindex)
{
LOCK(cs);
Expand All @@ -201,29 +253,55 @@ void CStakingManager::UpdatedBlockTip(const CBlockIndex* pindex)

void CStakingManager::DoMaintenance(CConnman& connman)
{
if (!fEnableStaking) return;
if (pwallet->IsLocked(true)) return;

const Consensus::Params& params = Params().GetConsensus();
if (!fEnableStaking) return; // Should never happen

CBlockIndex* pindexPrev = chainActive.Tip();
int nStakeHeight = pindexPrev->nHeight;
if (!pindexPrev)
bool fHaveConnections = !g_connman ? false : g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) > 0;
if (pwallet->IsLocked(true) || !pindexPrev || !masternodeSync.IsSynced() || !fHaveConnections || nReserveBalance >= pwallet->GetBalance()) {
nLastCoinStakeSearchInterval = 0;
MilliSleep(1 * 60 * 1000); // Wait 1 minute
return;
}

// Check block height
const bool fPosPhase = (nStakeHeight >= params.POSStartHeight);
if (nStakeHeight < params.POSStartHeight) {
const int nStakeHeight = pindexPrev->nHeight + 1;
const Consensus::Params& params = Params().GetConsensus();
const bool fPosPhase = (nStakeHeight >= params.POSStartHeight) || (nStakeHeight >= params.POSPOWStartHeight);

if (!fPosPhase) {
// no POS for at least 1 block
nLastCoinStakeSearchInterval = 0;
MilliSleep(1 * 60 * 1000); // Wait 1 minute
return;
}

//control the amount of times the client will check for mintable coins
//search our map of hashed blocks, see if bestblock has been hashed yet
if (mapHashedBlocks.count(chainActive.Tip()->nHeight) && !fLastLoopOrphan) {
// wait max 5 seconds if recently hashed
int nTimePast = GetTime() - mapHashedBlocks[chainActive.Tip()->nHeight];
if (nTimePast < nHashInterval && nTimePast >= 0) {
MilliSleep(std::min(nHashInterval - nTimePast, (unsigned int)5) * 1000);
return;
}
}
fLastLoopOrphan = false;

//control the amount of times the client will check for mintable coins
if (!MintableCoins()) {
// No mintable coins
nLastCoinStakeSearchInterval = 0;
LogPrint(BCLog::STAKING, "%s: No mintable coins, waiting..\n", __func__);
MilliSleep(5 * 60 * 1000); // Wait 5 minutes
return;
}

int64_t nSearchTime = GetAdjustedTime();
if (nSearchTime < nLastCoinStakeSearchTime) {
MilliSleep((nLastCoinStakeSearchTime - nSearchTime) * 1000); // Wait
return;
} else {
nLastCoinStakeSearchInterval = nSearchTime - nLastCoinStakeSearchTime;
nLastCoinStakeSearchTime = nSearchTime;
}
// Create new block
std::shared_ptr<CMutableTransaction> coinstakeTxPtr = std::shared_ptr<CMutableTransaction>(new CMutableTransaction);
std::shared_ptr<CStakeInput> coinstakeInputPtr = nullptr;
Expand All @@ -233,7 +311,8 @@ void CStakingManager::DoMaintenance(CConnman& connman)
try {
pblocktemplate = BlockAssembler(Params()).CreateNewBlock(CScript(), coinstakeTxPtr, coinstakeInputPtr);
} catch (const std::exception& e) {
LogPrint(BCLog::STAKING, "%s: error creating block - %s", __func__, e.what());
LogPrint(BCLog::STAKING, "%s: error creating block, waiting.. - %s", __func__, e.what());
MilliSleep(1 * 60 * 1000); // Wait 1 minute
return;
}
} else {
Expand Down Expand Up @@ -263,6 +342,8 @@ void CStakingManager::DoMaintenance(CConnman& connman)
/// Process block
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
if (!ProcessNewBlock(Params(), shared_pblock, true, nullptr)) {
fLastLoopOrphan = true;
LogPrint(BCLog::STAKING, "%s: ProcessNewBlock, block not accepted", __func__);
MilliSleep(10 * 1000); // Wait 10 seconds
}
}
9 changes: 9 additions & 0 deletions src/pos/staking-manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class CMutableTransaction;
class CStakeInput;
class CStakingManager;
class CWallet;
class uint256;

extern std::shared_ptr<CStakingManager> stakingManager;

Expand All @@ -29,9 +30,15 @@ class CStakingManager
const CBlockIndex* tipIndex{nullptr};
CWallet* pwallet = nullptr;

std::map<unsigned int, unsigned int> mapHashedBlocks;

int64_t nMintableLastCheck;
bool fMintableCoins;
bool fLastLoopOrphan;
int64_t nLastCoinStakeSearchInterval;
int64_t nLastCoinStakeSearchTime;
unsigned int nExtraNonce;
const unsigned int nHashInterval;

public:
CStakingManager(CWallet * const pwalletIn = nullptr);
Expand All @@ -43,6 +50,8 @@ class CStakingManager
bool MintableCoins();
bool SelectStakeCoins(std::list<std::unique_ptr<CStakeInput> >& listInputs, CAmount nTargetAmount, int blockHeight);
bool CreateCoinStake(const CBlockIndex* pindexPrev, std::shared_ptr<CMutableTransaction>& coinstakeTx, std::shared_ptr<CStakeInput>& coinstakeInput);
bool Stake(const CBlockIndex* pindexPrev, CStakeInput* stakeInput, unsigned int nBits, unsigned int& nTimeTx, uint256& hashProofOfStake);
bool IsStaking();

void UpdatedBlockTip(const CBlockIndex* pindex);

Expand Down
33 changes: 17 additions & 16 deletions src/wallet/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3371,24 +3371,25 @@ UniValue getstakingstatus(const JSONRPCRequest& request)

LOCK2(cs_main, pwallet->cs_wallet);

bool fValidTime = chainActive.Tip()->nTime > 1471482000;
bool fHaveConnections = !g_connman ? false : g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) > 0;
bool fWalletUnlocked = !pwallet->IsLocked();
bool fMintableCoins = stakingManager->MintableCoins();
bool fEnoughCoins = stakingManager->nReserveBalance <= pwallet->GetBalance();
bool fMnSync = masternodeSync.IsSynced();
bool fStakingStatus = stakingManager->IsStaking();

UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("validtime", chainActive.Tip()->nTime > 1471482000));
obj.push_back(Pair("haveconnections", !g_connman ? false : g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) > 0));
obj.push_back(Pair("validtime", fValidTime));
obj.push_back(Pair("haveconnections", fHaveConnections));
if (pwallet) {
obj.push_back(Pair("walletunlocked", !pwallet->IsLocked()));
obj.push_back(Pair("mintablecoins", stakingManager->MintableCoins()));
obj.push_back(Pair("enoughcoins", stakingManager->nReserveBalance <= pwallet->GetBalance()));
}
obj.push_back(Pair("mnsync", masternodeSync.IsSynced()));

bool nStaking = false;
/*
if (mapHashedBlocks.count(chainActive.Tip()->nHeight))
nStaking = true;
else if (mapHashedBlocks.count(chainActive.Tip()->nHeight - 1) && nLastCoinStakeSearchInterval)
nStaking = true;
obj.push_back(Pair("staking status", nStaking));
*/
obj.push_back(Pair("walletunlocked", fWalletUnlocked));
obj.push_back(Pair("mintablecoins", fMintableCoins));
obj.push_back(Pair("enoughcoins", fEnoughCoins));
}
obj.push_back(Pair("mnsync", fMnSync));
obj.push_back(Pair("staking_status", fStakingStatus));

return obj;
}

Expand Down

0 comments on commit 01f32a5

Please sign in to comment.