Merge pull request #1009 from UdjinM6/lastPaid

Improve mn winners detection
This commit is contained in:
Evan Duffield 2016-09-13 08:35:59 -07:00 committed by GitHub
commit 6f84dc15c0
8 changed files with 148 additions and 106 deletions

View File

@ -302,7 +302,7 @@ void CMasternodePayments::ProcessMessage(CNode* pfrom, std::string& strCommand,
return;
}
int nFirstBlock = pCurrentBlockIndex->nHeight - mnodeman.CountEnabled()*1.25;
int nFirstBlock = pCurrentBlockIndex->nHeight - GetStorageLimit();
if(winner.nBlockHeight < nFirstBlock || winner.nBlockHeight > pCurrentBlockIndex->nHeight+20) {
LogPrint("mnpayments", "MNWINNER -- winner out of range: nFirstBlock=%d, nBlockHeight=%d, nHeight=%d\n", nFirstBlock, winner.nBlockHeight, pCurrentBlockIndex->nHeight);
return;
@ -692,8 +692,8 @@ void CMasternodePayments::Sync(CNode* node, int nCountNeeded)
if(!pCurrentBlockIndex) return;
int nCount = (mnodeman.CountEnabled()*1.25);
if(nCountNeeded > nCount) nCountNeeded = nCount;
int nLimit = GetStorageLimit();
if(nCountNeeded > nLimit) nCountNeeded = nLimit;
int nInvCount = 0;
std::map<uint256, CMasternodePaymentWinner>::iterator it = mapMasternodePayeeVotes.begin();
@ -768,6 +768,11 @@ bool CMasternodePayments::IsEnoughData(int nMnCount) {
return false;
}
int CMasternodePayments::GetStorageLimit()
{
return std::max(int(mnodeman.size() * nStorageCoeff), nMinBlocksToStore);
}
void CMasternodePayments::UpdatedBlockTip(const CBlockIndex *pindex)
{
pCurrentBlockIndex = pindex;
@ -776,4 +781,6 @@ void CMasternodePayments::UpdatedBlockTip(const CBlockIndex *pindex)
if (!fLiteMode && masternodeSync.IsMasternodeListSynced()) {
ProcessBlock(pindex->nHeight + 10);
}
// normal wallet does not need to update this every block, doing update on rpc call should be enough
if(fMasterNode) mnodeman.UpdateLastPaid(pindex);
}

View File

@ -272,6 +272,7 @@ public:
}
bool IsEnoughData(int nMnCount);
int GetStorageLimit();
ADD_SERIALIZE_METHODS;

View File

@ -296,7 +296,7 @@ void CMasternodeSync::ProcessTick()
// check for data
// if mnpayments already has enough blocks and votes, switch to the next asset
// try to fetch data from at least two peers though
if(nRequestedMasternodeAttempt > 1 && mnpayments.IsEnoughData(nMnCount)) {
if(nRequestedMasternodeAttempt > 1 && mnpayments.IsEnoughData(mnpayments.GetStorageLimit())) {
LogPrintf("CMasternodeSync::Process -- nTick %d nRequestedMasternodeAssets %d -- found enough data\n", nTick, nRequestedMasternodeAssets);
SwitchToNextAsset();
return;
@ -309,7 +309,7 @@ void CMasternodeSync::ProcessTick()
if(pnode->nVersion < mnpayments.GetMinMasternodePaymentsProto()) continue;
nRequestedMasternodeAttempt++;
pnode->PushMessage(NetMsgType::MNWINNERSSYNC, nMnCount); //sync payees
pnode->PushMessage(NetMsgType::MNWINNERSSYNC, mnpayments.GetStorageLimit()); //sync payees
return; //this will cause each peer to get one request each six seconds for the various assets we need

View File

@ -30,8 +30,9 @@ CMasternode::CMasternode()
activeState = MASTERNODE_ENABLED;
sigTime = GetAdjustedTime();
lastPing = CMasternodePing();
cacheInputAge = 0;
cacheInputAgeBlock = 0;
nTimeLastPaid = 0;
nBlockLastPaid = 0;
nCacheCollateralBlock = 0;
unitTest = false;
allowFreeTx = true;
protocolVersion = PROTOCOL_VERSION;
@ -52,8 +53,9 @@ CMasternode::CMasternode(const CMasternode& other)
activeState = other.activeState;
sigTime = other.sigTime;
lastPing = other.lastPing;
cacheInputAge = other.cacheInputAge;
cacheInputAgeBlock = other.cacheInputAgeBlock;
nTimeLastPaid = other.nTimeLastPaid;
nBlockLastPaid = other.nBlockLastPaid;
nCacheCollateralBlock = other.nCacheCollateralBlock;
unitTest = other.unitTest;
allowFreeTx = other.allowFreeTx;
protocolVersion = other.protocolVersion;
@ -74,8 +76,9 @@ CMasternode::CMasternode(const CMasternodeBroadcast& mnb)
activeState = MASTERNODE_ENABLED;
sigTime = mnb.sigTime;
lastPing = mnb.lastPing;
cacheInputAge = 0;
cacheInputAgeBlock = 0;
nTimeLastPaid = 0;
nBlockLastPaid = 0;
nCacheCollateralBlock = 0;
unitTest = false;
allowFreeTx = true;
protocolVersion = mnb.protocolVersion;
@ -200,65 +203,64 @@ void CMasternode::Check(bool forceCheck)
activeState = MASTERNODE_ENABLED; // OK
}
int64_t CMasternode::SecondsSincePayment() {
int64_t sec = (GetAdjustedTime() - GetLastPaid());
int64_t month = 60*60*24*30;
if(sec < month) return sec; //if it's less than 30 days, give seconds
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
ss << vin;
ss << sigTime;
uint256 hash = ss.GetHash();
// return some deterministic value for unknown/unpaid but force it to be more than 30 days old
return month + UintToArith256(hash).GetCompact(false);
}
int64_t CMasternode::GetLastPaid() {
CBlockIndex *pindexPrev = NULL;
int CMasternode::GetCollateralAge()
{
int nHeight;
{
LOCK(cs_main);
pindexPrev = chainActive.Tip();
if(!pindexPrev) return 0;
TRY_LOCK(cs_main, lockMain);
if(!lockMain || !chainActive.Tip()) return -1;
nHeight = chainActive.Height();
}
CScript mnpayee;
mnpayee = GetScriptForDestination(pubkey.GetID());
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
ss << vin;
ss << sigTime;
uint256 hash = ss.GetHash();
// use a deterministic offset to break a tie -- 2.5 minutes
int64_t nOffset = UintToArith256(hash).GetCompact(false) % 150;
const CBlockIndex *BlockReading = pindexPrev;
int nMnCount = mnodeman.CountEnabled()*1.25;
int n = 0;
for (unsigned int i = 1; BlockReading && BlockReading->nHeight > 0; i++) {
if(n >= nMnCount){
return 0;
if (nCacheCollateralBlock == 0) {
int nInputAge = GetInputAge(vin);
if(nInputAge > 0) {
nCacheCollateralBlock = nHeight - nInputAge;
} else {
return nInputAge;
}
n++;
}
if(mnpayments.mapMasternodeBlocks.count(BlockReading->nHeight)){
/*
Search for this payee, with at least 2 votes. This will aid in consensus allowing the network
to converge on the same payees quickly, then keep the same schedule.
*/
if(mnpayments.mapMasternodeBlocks[BlockReading->nHeight].HasPayeeWithVotes(mnpayee, 2)){
return BlockReading->nTime + nOffset;
}
return nHeight - nCacheCollateralBlock;
}
void CMasternode::UpdateLastPaid(const CBlockIndex *pindex, int nMaxBlocksToScanBack)
{
if(!pindex) return;
const CBlockIndex *BlockReading = pindex;
CScript mnpayee = GetScriptForDestination(pubkey.GetID());
// LogPrint("masternode", "CMasternode::UpdateLastPaidBlock -- searching for block with payment to %s\n", vin.prevout.ToStringShort());
LOCK(cs_mapMasternodeBlocks);
for (int i = 0; BlockReading && BlockReading->nHeight > nBlockLastPaid && i < nMaxBlocksToScanBack; i++) {
if(mnpayments.mapMasternodeBlocks.count(BlockReading->nHeight) &&
mnpayments.mapMasternodeBlocks[BlockReading->nHeight].HasPayeeWithVotes(mnpayee, 2))
{
CBlock block;
if(!ReadBlockFromDisk(block, BlockReading, Params().GetConsensus())) // shouldn't really happen
continue;
CAmount nMasternodePayment = GetMasternodePayment(BlockReading->nHeight, block.vtx[0].GetValueOut());
BOOST_FOREACH(CTxOut txout, block.vtx[0].vout)
if(mnpayee == txout.scriptPubKey && nMasternodePayment == txout.nValue) {
nBlockLastPaid = BlockReading->nHeight;
nTimeLastPaid = BlockReading->nTime;
LogPrint("masternode", "CMasternode::UpdateLastPaidBlock -- searching for block with payment to %s -- found new %d\n", vin.prevout.ToStringShort(), nBlockLastPaid);
return;
}
}
if (BlockReading->pprev == NULL) { assert(BlockReading); break; }
BlockReading = BlockReading->pprev;
}
return 0;
// Last payment for this masternode wasn't found in latest mnpayments blocks
// or it was found in mnpayments blocks but wasn't found in the blockchain.
// LogPrint("masternode", "CMasternode::UpdateLastPaidBlock -- searching for block with payment to %s -- keeping old %d\n", vin.prevout.ToStringShort(), nBlockLastPaid);
}
CMasternodeBroadcast::CMasternodeBroadcast()
@ -271,8 +273,9 @@ CMasternodeBroadcast::CMasternodeBroadcast()
activeState = MASTERNODE_ENABLED;
sigTime = GetAdjustedTime();
lastPing = CMasternodePing();
cacheInputAge = 0;
cacheInputAgeBlock = 0;
nTimeLastPaid = 0;
nBlockLastPaid = 0;
nCacheCollateralBlock = 0;
unitTest = false;
allowFreeTx = true;
protocolVersion = PROTOCOL_VERSION;
@ -291,8 +294,9 @@ CMasternodeBroadcast::CMasternodeBroadcast(CService newAddr, CTxIn newVin, CPubK
activeState = MASTERNODE_ENABLED;
sigTime = GetAdjustedTime();
lastPing = CMasternodePing();
cacheInputAge = 0;
cacheInputAgeBlock = 0;
nTimeLastPaid = 0;
nBlockLastPaid = 0;
nCacheCollateralBlock = 0;
unitTest = false;
allowFreeTx = true;
protocolVersion = protocolVersionIn;
@ -311,8 +315,9 @@ CMasternodeBroadcast::CMasternodeBroadcast(const CMasternode& mn)
activeState = mn.activeState;
sigTime = mn.sigTime;
lastPing = mn.lastPing;
cacheInputAge = mn.cacheInputAge;
cacheInputAgeBlock = mn.cacheInputAgeBlock;
nTimeLastPaid = mn.nTimeLastPaid;
nBlockLastPaid = mn.nBlockLastPaid;
nCacheCollateralBlock = mn.nCacheCollateralBlock;
unitTest = mn.unitTest;
allowFreeTx = mn.allowFreeTx;
protocolVersion = mn.protocolVersion;

View File

@ -122,8 +122,9 @@ public:
std::vector<unsigned char> vchSig;
int activeState;
int64_t sigTime; //mnb message time
int cacheInputAge;
int cacheInputAgeBlock;
int64_t nTimeLastPaid;
int nBlockLastPaid;
int nCacheCollateralBlock;
bool unitTest;
bool allowFreeTx;
int protocolVersion;
@ -155,8 +156,9 @@ public:
swap(first.activeState, second.activeState);
swap(first.sigTime, second.sigTime);
swap(first.lastPing, second.lastPing);
swap(first.cacheInputAge, second.cacheInputAge);
swap(first.cacheInputAgeBlock, second.cacheInputAgeBlock);
swap(first.nTimeLastPaid, second.nTimeLastPaid);
swap(first.nBlockLastPaid, second.nBlockLastPaid);
swap(first.nCacheCollateralBlock, second.nCacheCollateralBlock);
swap(first.unitTest, second.unitTest);
swap(first.allowFreeTx, second.allowFreeTx);
swap(first.protocolVersion, second.protocolVersion);
@ -204,8 +206,9 @@ public:
READWRITE(protocolVersion);
READWRITE(activeState);
READWRITE(lastPing);
READWRITE(cacheInputAge);
READWRITE(cacheInputAgeBlock);
READWRITE(nTimeLastPaid);
READWRITE(nBlockLastPaid);
READWRITE(nCacheCollateralBlock);
READWRITE(unitTest);
READWRITE(allowFreeTx);
READWRITE(nLastDsq);
@ -257,19 +260,6 @@ public:
return activeState == MASTERNODE_PRE_ENABLED;
}
int GetMasternodeInputAge()
{
LOCK(cs_main);
if(chainActive.Tip() == NULL) return 0;
if(cacheInputAge == 0){
cacheInputAge = GetInputAge(vin);
cacheInputAgeBlock = chainActive.Tip()->nHeight;
}
return cacheInputAge + (chainActive.Tip()->nHeight - cacheInputAgeBlock);
}
std::string Status() {
std::string strStatus = "unknown";
@ -283,7 +273,11 @@ public:
return strStatus;
}
int64_t GetLastPaid();
int GetCollateralAge();
int GetLastPaidTime() { return nTimeLastPaid; }
int GetLastPaidBlock() { return nBlockLastPaid; }
void UpdateLastPaid(const CBlockIndex *pindex, int nMaxBlocksToScanBack);
};

View File

@ -17,12 +17,12 @@
/** Masternode manager */
CMasternodeMan mnodeman;
struct CompareLastPaid
struct CompareLastPaidBlock
{
bool operator()(const pair<int64_t, CTxIn>& t1,
const pair<int64_t, CTxIn>& t2) const
bool operator()(const std::pair<int, CTxIn>& t1,
const std::pair<int, CTxIn>& t2) const
{
return t1.first < t2.first;
return (t1.first != t2.first) ? (t1.first < t2.first) : (t1.second < t2.second);
}
};
@ -311,7 +311,7 @@ CMasternode* CMasternodeMan::GetNextMasternodeInQueueForPayment(int nBlockHeight
LOCK(cs);
CMasternode *pBestMasternode = NULL;
std::vector<pair<int64_t, CTxIn> > vecMasternodeLastPaid;
std::vector<std::pair<int, CTxIn> > vecMasternodeLastPaid;
/*
Make a vector with all of the last paid times
@ -333,9 +333,9 @@ CMasternode* CMasternodeMan::GetNextMasternodeInQueueForPayment(int nBlockHeight
if(fFilterSigTime && mn.sigTime + (nMnCount*2.6*60) > GetAdjustedTime()) continue;
//make sure it has as many confirmations as there are masternodes
if(mn.GetMasternodeInputAge() < nMnCount) continue;
if(mn.GetCollateralAge() < nMnCount) continue;
vecMasternodeLastPaid.push_back(make_pair(mn.SecondsSincePayment(), mn.vin));
vecMasternodeLastPaid.push_back(std::make_pair(mn.GetLastPaidBlock(), mn.vin));
}
nCount = (int)vecMasternodeLastPaid.size();
@ -343,17 +343,17 @@ CMasternode* CMasternodeMan::GetNextMasternodeInQueueForPayment(int nBlockHeight
//when the network is in the process of upgrading, don't penalize nodes that recently restarted
if(fFilterSigTime && nCount < nMnCount/3) return GetNextMasternodeInQueueForPayment(nBlockHeight, false, nCount);
// Sort them high to low
sort(vecMasternodeLastPaid.rbegin(), vecMasternodeLastPaid.rend(), CompareLastPaid());
// Sort them low to high
sort(vecMasternodeLastPaid.begin(), vecMasternodeLastPaid.end(), CompareLastPaidBlock());
// Look at 1/10 of the oldest nodes (by last payment), calculate their scores and pay the best one
// -- This doesn't look at who is being paid in the +8-10 blocks, allowing for double payments very rarely
// -- 1/100 payments should be a double payment on mainnet - (1/(3000/10))*2
// -- (chance per block * chances before IsScheduled will fire)
int nTenthNetwork = CountEnabled()/10;
int nCountTenth = 0;
int nCountTenth = 0;
arith_uint256 nHigh = 0;
BOOST_FOREACH (PAIRTYPE(int64_t, CTxIn)& s, vecMasternodeLastPaid){
BOOST_FOREACH (PAIRTYPE(int, CTxIn)& s, vecMasternodeLastPaid){
CMasternode* pmn = Find(s.second);
if(!pmn) break;
@ -769,3 +769,22 @@ bool CMasternodeMan::CheckMnbAndUpdateMasternodeList(CMasternodeBroadcast mnb, i
return true;
}
void CMasternodeMan::UpdateLastPaid(const CBlockIndex *pindex) {
if(fLiteMode) return;
static bool IsFirstRun = true;
// Do full scan on first run or if we are not a masternode
// (MNs should update this info on every block, so limited scan should be enough for them)
int nMaxBlocksToScanBack = (IsFirstRun || !fMasterNode) ? mnpayments.GetStorageLimit() : MASTERNODES_LAST_PAID_SCAN_BLOCKS;
// LogPrint("mnpayments", "CMasternodeMan::UpdateLastPaid -- nHeight=%d, nMaxBlocksToScanBack=%d, IsFirstRun=%s\n",
// pindex->nHeight, nMaxBlocksToScanBack, IsFirstRun ? "true" : "false");
BOOST_FOREACH(CMasternode& mn, vMasternodes) {
mn.UpdateLastPaid(pindex, nMaxBlocksToScanBack);
}
// every time is like the first time if winners list is not synced
IsFirstRun = !masternodeSync.IsWinnersListSynced();
}

View File

@ -24,6 +24,8 @@ extern CMasternodeMan mnodeman;
class CMasternodeMan
{
private:
static const int MASTERNODES_LAST_PAID_SCAN_BLOCKS = 100;
// critical section to protect the inner data structures
mutable CCriticalSection cs;
@ -134,6 +136,7 @@ public:
/// Perform complete check and only then update list and maps
bool CheckMnbAndUpdateMasternodeList(CMasternodeBroadcast mnb, int& nDos);
void UpdateLastPaid(const CBlockIndex *pindex);
};
#endif

View File

@ -477,9 +477,10 @@ UniValue masternodelist(const UniValue& params, bool fHelp)
if (params.size() >= 1) strMode = params[0].get_str();
if (params.size() == 2) strFilter = params[1].get_str();
if (fHelp ||
(strMode != "status" && strMode != "vin" && strMode != "pubkey" && strMode != "lastseen" && strMode != "activeseconds" && strMode != "rank" && strMode != "addr"
&& strMode != "protocol" && strMode != "full" && strMode != "lastpaid"))
if (fHelp || (
strMode != "activeseconds" && strMode != "addr" && strMode != "full" &&
strMode != "lastseen" && strMode != "lastpaidtime" && strMode != "lastpaidblock" &&
strMode != "protocol" && strMode != "pubkey" && strMode != "rank" && strMode != "status"))
{
throw runtime_error(
"masternodelist ( \"mode\" \"filter\" )\n"
@ -492,10 +493,11 @@ UniValue masternodelist(const UniValue& params, bool fHelp)
" activeseconds - Print number of seconds masternode recognized by the network as enabled\n"
" (since latest issued \"masternode start/start-many/start-alias\")\n"
" addr - Print ip address associated with a masternode (can be additionally filtered, partial match)\n"
" full - Print info in format 'status protocol pubkey IP lastseen activeseconds lastpaid'\n"
" full - Print info in format 'status protocol pubkey IP lastseen activeseconds lastpaidtime'\n"
" (can be additionally filtered, partial match)\n"
" lastseen - Print timestamp of when a masternode was last seen on the network\n"
" lastpaid - The last time a node was paid on the network\n"
" lastpaidblock - Print the last block height a node was paid on the network\n"
" lastpaidtime - Print the last time a node was paid on the network\n"
" protocol - Print protocol of a masternode (can be additionally filtered, exact match))\n"
" pubkey - Print public key associated with a masternode (can be additionally filtered,\n"
" partial match)\n"
@ -505,6 +507,15 @@ UniValue masternodelist(const UniValue& params, bool fHelp)
);
}
if (strMode == "full" || strMode == "lastpaidtime" || strMode == "lastpaidblock") {
CBlockIndex* pindex;
{
LOCK(cs_main);
pindex = chainActive.Tip();
}
mnodeman.UpdateLastPaid(pindex);
}
UniValue obj(UniValue::VOBJ);
if (strMode == "rank") {
std::vector<pair<int, CMasternode> > vMasternodeRanks = mnodeman.GetMasternodeRanks(chainActive.Tip()->nHeight);
@ -536,7 +547,7 @@ UniValue masternodelist(const UniValue& params, bool fHelp)
mn.addr.ToString() << " " <<
(int64_t)mn.lastPing.sigTime << " " << setw(8) <<
(int64_t)(mn.lastPing.sigTime - mn.sigTime) << " " <<
(int64_t)mn.GetLastPaid();
(int64_t)mn.GetLastPaidTime();
std::string output = stringStream.str();
stringStream << " " << strVin;
if(strFilter !="" && stringStream.str().find(strFilter) == string::npos &&
@ -545,10 +556,12 @@ UniValue masternodelist(const UniValue& params, bool fHelp)
} else if (strMode == "lastseen") {
if(strFilter !="" && strVin.find(strFilter) == string::npos) continue;
obj.push_back(Pair(strVin, (int64_t)mn.lastPing.sigTime));
} else if (strMode == "lastpaid"){
if(strFilter !="" && mn.vin.prevout.hash.ToString().find(strFilter) == string::npos &&
strVin.find(strFilter) == string::npos) continue;
obj.push_back(Pair(strVin, (int64_t)mn.GetLastPaid()));
} else if (strMode == "lastpaidblock") {
if (strFilter !="" && strVin.find(strFilter) == std::string::npos) continue;
obj.push_back(Pair(strVin, mn.GetLastPaidBlock()));
} else if (strMode == "lastpaidtime") {
if (strFilter !="" && strVin.find(strFilter) == std::string::npos) continue;
obj.push_back(Pair(strVin, mn.GetLastPaidTime()));
} else if (strMode == "protocol") {
if(strFilter !="" && strFilter != strprintf("%d", mn.protocolVersion) &&
strVin.find(strFilter) == string::npos) continue;