diff --git a/src/main.cpp b/src/main.cpp index f0fa50081d99..e368c61fc41d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4939,6 +4939,12 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) case MSG_MASTERNODE_WINNER: return mnpayments.mapMasternodePayeeVotes.count(inv.hash); + case MSG_MASTERNODE_WINNER_BLOCK: + { + BlockMap::iterator mi = mapBlockIndex.find(inv.hash); + return mi != mapBlockIndex.end() && mnpayments.mapMasternodeBlocks.find(mi->second->nHeight) != mnpayments.mapMasternodeBlocks.end(); + } + case MSG_MASTERNODE_ANNOUNCE: return mnodeman.mapSeenMasternodeBroadcast.count(inv.hash); @@ -5105,7 +5111,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam pushed = true; } } - + if (!pushed && inv.type == MSG_MASTERNODE_WINNER) { if(mnpayments.mapMasternodePayeeVotes.count(inv.hash)) { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); @@ -5116,6 +5122,25 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } + if (!pushed && inv.type == MSG_MASTERNODE_WINNER_BLOCK) { + BlockMap::iterator mi = mapBlockIndex.find(inv.hash); + LOCK(cs_mapMasternodeBlocks); + if (mi != mapBlockIndex.end() && mnpayments.mapMasternodeBlocks.count(mi->second->nHeight)) { + BOOST_FOREACH(CMasternodePayee& payee, mnpayments.mapMasternodeBlocks[mi->second->nHeight].vecPayees) { + std::vector vecVoteHashes = payee.GetVoteHashes(); + BOOST_FOREACH(uint256& hash, vecVoteHashes) { + if(mnpayments.mapMasternodePayeeVotes.count(hash)) { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << mnpayments.mapMasternodePayeeVotes[hash]; + pfrom->PushMessage(NetMsgType::MNWINNER, ss); + } + } + } + pushed = true; + } + } + if (!pushed && inv.type == MSG_MASTERNODE_ANNOUNCE) { if(mnodeman.mapSeenMasternodeBroadcast.count(inv.hash)){ CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); diff --git a/src/masternode-payments.cpp b/src/masternode-payments.cpp index b9b394fd40ba..36802209fae9 100644 --- a/src/masternode-payments.cpp +++ b/src/masternode-payments.cpp @@ -439,12 +439,12 @@ void CMasternodeBlockPayees::AddPayee(CMasternodePaymentWinner winner) LOCK(cs_vecPayees); BOOST_FOREACH(CMasternodePayee& payee, vecPayees) { - if (payee.scriptPubKey == winner.payee) { - payee.nVotes++; + if (payee.GetPayee() == winner.payee) { + payee.AddVoteHash(winner.GetHash()); return; } } - CMasternodePayee payeeNew(winner.payee, 1); + CMasternodePayee payeeNew(winner.payee, winner.GetHash()); vecPayees.push_back(payeeNew); } @@ -459,9 +459,9 @@ bool CMasternodeBlockPayees::GetPayee(CScript& payeeRet) int nVotes = -1; BOOST_FOREACH(CMasternodePayee& payee, vecPayees) { - if (payee.nVotes > nVotes) { - payeeRet = payee.scriptPubKey; - nVotes = payee.nVotes; + if (payee.GetVoteCount() > nVotes) { + payeeRet = payee.GetPayee(); + nVotes = payee.GetVoteCount(); } } @@ -473,7 +473,7 @@ bool CMasternodeBlockPayees::HasPayeeWithVotes(CScript payeeIn, int nVotesReq) LOCK(cs_vecPayees); BOOST_FOREACH(CMasternodePayee& payee, vecPayees) { - if (payee.nVotes >= nVotesReq && payee.scriptPubKey == payeeIn) { + if (payee.GetVoteCount() >= nVotesReq && payee.GetPayee() == payeeIn) { return true; } } @@ -494,8 +494,8 @@ bool CMasternodeBlockPayees::IsTransactionValid(const CTransaction& txNew) //require at least MNPAYMENTS_SIGNATURES_REQUIRED signatures BOOST_FOREACH(CMasternodePayee& payee, vecPayees) { - if (payee.nVotes >= nMaxSignatures) { - nMaxSignatures = payee.nVotes; + if (payee.GetVoteCount() >= nMaxSignatures) { + nMaxSignatures = payee.GetVoteCount(); } } @@ -503,16 +503,16 @@ bool CMasternodeBlockPayees::IsTransactionValid(const CTransaction& txNew) if(nMaxSignatures < MNPAYMENTS_SIGNATURES_REQUIRED) return true; BOOST_FOREACH(CMasternodePayee& payee, vecPayees) { - if (payee.nVotes >= MNPAYMENTS_SIGNATURES_REQUIRED) { + if (payee.GetVoteCount() >= MNPAYMENTS_SIGNATURES_REQUIRED) { BOOST_FOREACH(CTxOut txout, txNew.vout) { - if (payee.scriptPubKey == txout.scriptPubKey && nMasternodePayment == txout.nValue) { + if (payee.GetPayee() == txout.scriptPubKey && nMasternodePayment == txout.nValue) { LogPrint("mnpayments", "CMasternodeBlockPayees::IsTransactionValid -- Found required payment\n"); return true; } } CTxDestination address1; - ExtractDestination(payee.scriptPubKey, address1); + ExtractDestination(payee.GetPayee(), address1); CBitcoinAddress address2(address1); if(strPayeesPossible == "") { @@ -536,13 +536,13 @@ std::string CMasternodeBlockPayees::GetRequiredPaymentsString() BOOST_FOREACH(CMasternodePayee& payee, vecPayees) { CTxDestination address1; - ExtractDestination(payee.scriptPubKey, address1); + ExtractDestination(payee.GetPayee(), address1); CBitcoinAddress address2(address1); if (strRequiredPayments != "Unknown") { - strRequiredPayments += ", " + address2.ToString() + ":" + boost::lexical_cast(payee.nVotes); + strRequiredPayments += ", " + address2.ToString() + ":" + boost::lexical_cast(payee.GetVoteCount()); } else { - strRequiredPayments = address2.ToString() + ":" + boost::lexical_cast(payee.nVotes); + strRequiredPayments = address2.ToString() + ":" + boost::lexical_cast(payee.GetVoteCount()); } } @@ -743,30 +743,101 @@ std::string CMasternodePaymentWinner::ToString() const return info.str(); } +// Send all votes up to nCountNeeded blocks (but not more than GetStorageLimit) void CMasternodePayments::Sync(CNode* pnode, int nCountNeeded) { - LOCK(cs_mapMasternodePayeeVotes); + LOCK(cs_mapMasternodeBlocks); if(!pCurrentBlockIndex) return; - int nLimit = GetStorageLimit(); - if(nCountNeeded > nLimit) nCountNeeded = nLimit; + if(pnode->nVersion < 70201) { + // Old nodes can only sync via heavy method + int nLimit = GetStorageLimit(); + if(nCountNeeded > nLimit) nCountNeeded = nLimit; + } else { + // New nodes request missing payment blocks themselves, push only future winners to them + nCountNeeded = 0; + } int nInvCount = 0; - std::map::iterator it = mapMasternodePayeeVotes.begin(); - while(it != mapMasternodePayeeVotes.end()) { - CMasternodePaymentWinner winner = (*it).second; - if(winner.nBlockHeight >= pCurrentBlockIndex->nHeight - nCountNeeded && winner.nBlockHeight <= pCurrentBlockIndex->nHeight + 20) { - pnode->PushInventory(CInv(MSG_MASTERNODE_WINNER, winner.GetHash())); - nInvCount++; + + for(int h = pCurrentBlockIndex->nHeight - nCountNeeded; h < pCurrentBlockIndex->nHeight + 20; h++) { + if(mapMasternodeBlocks.count(h)) { + BOOST_FOREACH(CMasternodePayee& payee, mapMasternodeBlocks[h].vecPayees) { + std::vector vecVoteHashes = payee.GetVoteHashes(); + BOOST_FOREACH(uint256& hash, vecVoteHashes) { + pnode->PushInventory(CInv(MSG_MASTERNODE_WINNER, hash)); + nInvCount++; + } + } } - ++it; } LogPrintf("CMasternodePayments::Sync -- Sent %d winners to peer %d\n", nInvCount, pnode->id); pnode->PushMessage(NetMsgType::SYNCSTATUSCOUNT, MASTERNODE_SYNC_MNW, nInvCount); } +// Request low data payment blocks in batches directly from some node instead of/after preliminary Sync. +void CMasternodePayments::RequestLowDataPaymentBlocks(CNode* pnode) +{ + // Old nodes can't process this + if(pnode->nVersion < 70201) return; + + LOCK(cs_mapMasternodeBlocks); + + std::vector vToFetch; + std::map::iterator it = mapMasternodeBlocks.begin(); + + while(it != mapMasternodeBlocks.end()) { + int nTotalVotes = 0; + bool fFound = false; + BOOST_FOREACH(CMasternodePayee& payee, it->second.vecPayees) { + if(payee.GetVoteCount() >= MNPAYMENTS_SIGNATURES_REQUIRED) { + fFound = true; + break; + } + nTotalVotes += payee.GetVoteCount(); + } + // A clear winner (MNPAYMENTS_SIGNATURES_REQUIRED+ votes) was found + // or no clear winner was found but there are at least avg number of votes + if(fFound || nTotalVotes >= (MNPAYMENTS_SIGNATURES_TOTAL + MNPAYMENTS_SIGNATURES_REQUIRED)/2) { + // so just move to the next block + ++it; + continue; + } + // DEBUG + DBG ( + // Let's see why this failed + BOOST_FOREACH(CMasternodePayee& payee, it->second.vecPayees) { + CTxDestination address1; + ExtractDestination(payee.scriptPubKey, address1); + CBitcoinAddress address2(address1); + printf("payee %s votes %d\n", address2.ToString().c_str(), payee.GetVoteCount()); + } + printf("block %d votes total %d\n", it->first, nTotalVotes); + ) + // END DEBUG + // Low data block found, let's try to sync it + uint256 hash; + if(GetBlockHash(hash, it->first)) { + vToFetch.push_back(CInv(MSG_MASTERNODE_WINNER_BLOCK, hash)); + } + // We should not violate GETDATA rules + if(vToFetch.size() == MAX_INV_SZ) { + LogPrintf("CMasternodePayments::SyncLowDataPaymentBlocks -- asking peer %d for %d blocks\n", pnode->id, MAX_INV_SZ); + pnode->PushMessage(NetMsgType::GETDATA, vToFetch); + // Start filling new batch + vToFetch.clear(); + } + ++it; + } + // Ask for the rest of it + if(!vToFetch.empty()) { + LogPrintf("CMasternodePayments::SyncLowDataPaymentBlocks -- asking peer %d for %d blocks\n", pnode->id, vToFetch.size()); + pnode->PushMessage(NetMsgType::GETDATA, vToFetch); + } +} + std::string CMasternodePayments::ToString() const { std::ostringstream info; diff --git a/src/masternode-payments.h b/src/masternode-payments.h index 816c7e23d127..7f91f4ed2127 100644 --- a/src/masternode-payments.h +++ b/src/masternode-payments.h @@ -40,20 +40,36 @@ std::string GetRequiredPaymentsString(int nBlockHeight); class CMasternodePayee { -public: +private: CScript scriptPubKey; - int nVotes; + std::vector vecVoteHashes; - CMasternodePayee() : scriptPubKey(CScript()), nVotes(0) {} - CMasternodePayee(CScript payee, int nVotesIn) : scriptPubKey(payee), nVotes(nVotesIn) {} +public: + CMasternodePayee() : + scriptPubKey(), + vecVoteHashes() + {} + + CMasternodePayee(CScript payee, uint256 hashIn) : + scriptPubKey(payee), + vecVoteHashes() + { + vecVoteHashes.push_back(hashIn); + } ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(*(CScriptBase*)(&scriptPubKey)); - READWRITE(nVotes); - } + READWRITE(vecVoteHashes); + } + + CScript GetPayee() { return scriptPubKey; } + + void AddVoteHash(uint256 hashIn) { vecVoteHashes.push_back(hashIn); } + std::vector GetVoteHashes() { return vecVoteHashes; } + int GetVoteCount() { return vecVoteHashes.size(); } }; // Keep track of votes for payees from masternodes @@ -169,6 +185,7 @@ class CMasternodePayments bool ProcessBlock(int nBlockHeight); void Sync(CNode* node, int nCountNeeded); + void RequestLowDataPaymentBlocks(CNode* pnode); void CheckAndRemove(); bool GetBlockPayee(int nBlockHeight, CScript& payee); diff --git a/src/masternode-sync.cpp b/src/masternode-sync.cpp index a6cad7851fb3..2364ebf1229d 100644 --- a/src/masternode-sync.cpp +++ b/src/masternode-sync.cpp @@ -309,8 +309,10 @@ void CMasternodeSync::ProcessTick() if(pnode->nVersion < mnpayments.GetMinMasternodePaymentsProto()) continue; nRequestedMasternodeAttempt++; - pnode->PushMessage(NetMsgType::MNWINNERSSYNC, mnpayments.GetStorageLimit()); //sync payees - + // ask node for all winners it has (new nodes will only return future winners) + pnode->PushMessage(NetMsgType::MNWINNERSSYNC, mnpayments.GetStorageLimit()); + // ask node for missing pieces only (old nodes will not be asked) + mnpayments.RequestLowDataPaymentBlocks(pnode); return; //this will cause each peer to get one request each six seconds for the various assets we need } diff --git a/src/protocol.cpp b/src/protocol.cpp index 2f9579b2d039..5981dc2f5038 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -41,6 +41,7 @@ const char *TXLOCKVOTE="txlvote"; const char *SPORK="spork"; const char *GETSPORKS="getsporks"; const char *MNWINNER="mnw"; +const char *MNWINNERBLOCK="mnwb"; const char *MNWINNERSSYNC="mnget"; const char *MNSCANERROR="mn scan error"; // not implemented const char *MNBUDGETSYNC="mnvs"; // depreciated since 12.1 @@ -78,7 +79,7 @@ static const char* ppszTypeName[] = NetMsgType::TXLOCKVOTE, NetMsgType::SPORK, NetMsgType::MNWINNER, - NetMsgType::MNSCANERROR, // not implemented + NetMsgType::MNWINNERBLOCK, // reusing, was MNSCANERROR previousely, was NOT used in 12.0, we need this for inv NetMsgType::MNBUDGETVOTE, // depreciated since 12.1 NetMsgType::MNBUDGETPROPOSAL, // depreciated since 12.1 NetMsgType::MNBUDGETFINAL, // depreciated since 12.1 @@ -124,6 +125,7 @@ const static std::string allNetMessageTypes[] = { NetMsgType::SPORK, NetMsgType::GETSPORKS, NetMsgType::MNWINNER, + // NetMsgType::MNWINNERBLOCK, // there is no message for this, only inventory NetMsgType::MNWINNERSSYNC, NetMsgType::MNANNOUNCE, NetMsgType::MNPING, diff --git a/src/protocol.h b/src/protocol.h index 137ef6c073df..e2107aebcf00 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -346,7 +346,7 @@ enum { MSG_TXLOCK_VOTE, MSG_SPORK, MSG_MASTERNODE_WINNER, - MSG_MASTERNODE_SCANNING_ERROR, // not implemented + MSG_MASTERNODE_WINNER_BLOCK, // reusing, was MSG_MASTERNODE_SCANNING_ERROR previousely, was NOT used in 12.0 MSG_BUDGET_VOTE, // depreciated since 12.1 MSG_BUDGET_PROPOSAL, // depreciated since 12.1 MSG_BUDGET_FINALIZED, // depreciated since 12.1