New vote tallying implementation (#1135)

This commit is contained in:
Tim Flynn 2016-11-13 12:52:34 -05:00 committed by UdjinM6
parent 82ca5fdbb8
commit c31ba8ba4c
25 changed files with 2401 additions and 533 deletions

View File

@ -74,6 +74,8 @@ BITCOIN_CORE_H = \
arith_uint256.h \ arith_uint256.h \
base58.h \ base58.h \
bloom.h \ bloom.h \
cachemap.h \
cachemultimap.h \
chain.h \ chain.h \
chainparams.h \ chainparams.h \
chainparamsbase.h \ chainparamsbase.h \
@ -99,7 +101,9 @@ BITCOIN_CORE_H = \
darksend-relay.h \ darksend-relay.h \
governance.h \ governance.h \
governance-classes.h \ governance-classes.h \
governance-exceptions.h \
governance-vote.h \ governance-vote.h \
governance-votedb.h \
flat-database.h \ flat-database.h \
hash.h \ hash.h \
httprpc.h \ httprpc.h \
@ -202,6 +206,7 @@ libbitcoin_server_a_SOURCES = \
governance.cpp \ governance.cpp \
governance-classes.cpp \ governance-classes.cpp \
governance-vote.cpp \ governance-vote.cpp \
governance-votedb.cpp \
main.cpp \ main.cpp \
merkleblock.cpp \ merkleblock.cpp \
miner.cpp \ miner.cpp \

View File

@ -43,6 +43,8 @@ BITCOIN_TESTS =\
test/base64_tests.cpp \ test/base64_tests.cpp \
test/bip32_tests.cpp \ test/bip32_tests.cpp \
test/bloom_tests.cpp \ test/bloom_tests.cpp \
test/cachemap_tests.cpp \
test/cachemultimap_tests.cpp \
test/checkblock_tests.cpp \ test/checkblock_tests.cpp \
test/Checkpoints_tests.cpp \ test/Checkpoints_tests.cpp \
test/coins_tests.cpp \ test/coins_tests.cpp \

View File

@ -271,6 +271,7 @@ void CActiveMasternode::ManageStateLocal()
//update to masternode list //update to masternode list
LogPrintf("CActiveMasternode::ManageStateLocal -- Update Masternode List\n"); LogPrintf("CActiveMasternode::ManageStateLocal -- Update Masternode List\n");
mnodeman.UpdateMasternodeList(mnb); mnodeman.UpdateMasternodeList(mnb);
mnodeman.NotifyMasternodeUpdates();
//send to all peers //send to all peers
LogPrintf("CActiveMasternode::ManageStateLocal -- Relay broadcast, vin=%s\n", vin.ToString()); LogPrintf("CActiveMasternode::ManageStateLocal -- Relay broadcast, vin=%s\n", vin.ToString());

202
src/cachemap.h Normal file
View File

@ -0,0 +1,202 @@
// Copyright (c) 2014-2016 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef CACHEMAP_H_
#define CACHEMAP_H_
#include <map>
#include <list>
#include <cstddef>
#include "serialize.h"
/**
* Serializable structure for key/value items
*/
template<typename K, typename V>
struct CacheItem
{
CacheItem()
{}
CacheItem(const K& keyIn, const V& valueIn)
: key(keyIn),
value(valueIn)
{}
K key;
V value;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
{
READWRITE(key);
READWRITE(value);
}
};
/**
* Map like container that keeps the N most recently added items
*/
template<typename K, typename V, typename Size = uint32_t>
class CacheMap
{
public:
typedef Size size_type;
typedef CacheItem<K,V> item_t;
typedef std::list<item_t> list_t;
typedef typename list_t::iterator list_it;
typedef typename list_t::const_iterator list_cit;
typedef std::map<K, list_it> map_t;
typedef typename map_t::iterator map_it;
typedef typename map_t::const_iterator map_cit;
private:
size_type nMaxSize;
size_type nCurrentSize;
list_t listItems;
map_t mapIndex;
public:
CacheMap(size_type nMaxSizeIn = 0)
: nMaxSize(nMaxSizeIn),
nCurrentSize(0),
listItems(),
mapIndex()
{}
CacheMap(const CacheMap<K,V>& other)
: nMaxSize(other.nMaxSize),
nCurrentSize(other.nCurrentSize),
listItems(other.listItems),
mapIndex()
{
RebuildIndex();
}
void Clear()
{
mapIndex.clear();
listItems.clear();
nCurrentSize = 0;
}
void SetMaxSize(size_type nMaxSizeIn)
{
nMaxSize = nMaxSizeIn;
}
size_type GetMaxSize() const {
return nMaxSize;
}
size_type GetSize() const {
return nCurrentSize;
}
void Insert(const K& key, const V& value)
{
map_it it = mapIndex.find(key);
if(it != mapIndex.end()) {
item_t& item = *(it->second);
item.value = value;
return;
}
if(nCurrentSize == nMaxSize) {
PruneLast();
}
listItems.push_front(item_t(key, value));
mapIndex[key] = listItems.begin();
++nCurrentSize;
}
bool HasKey(const K& key) const
{
map_cit it = mapIndex.find(key);
return (it != mapIndex.end());
}
bool Get(const K& key, V& value) const
{
map_cit it = mapIndex.find(key);
if(it == mapIndex.end()) {
return false;
}
item_t& item = *(it->second);
value = item.value;
return true;
}
void Erase(const K& key)
{
map_it it = mapIndex.find(key);
if(it == mapIndex.end()) {
return;
}
listItems.erase(it->second);
mapIndex.erase(it);
--nCurrentSize;
}
const list_t& GetItemList() const {
return listItems;
}
CacheMap<K,V>& operator=(const CacheMap<K,V>& other)
{
nMaxSize = other.nMaxSize;
nCurrentSize = other.nCurrentSize;
listItems = other.listItems;
RebuildIndex();
return *this;
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
{
READWRITE(nMaxSize);
READWRITE(nCurrentSize);
READWRITE(listItems);
if(ser_action.ForRead()) {
RebuildIndex();
}
}
private:
void PruneLast()
{
if(nCurrentSize < 1) {
return;
}
item_t& item = listItems.back();
mapIndex.erase(item.key);
listItems.pop_back();
--nCurrentSize;
}
void RebuildIndex()
{
mapIndex.clear();
for(list_it it = listItems.begin(); it != listItems.end(); ++it) {
mapIndex[it->key] = it;
}
}
};
#endif /* CACHEMAP_H_ */

254
src/cachemultimap.h Normal file
View File

@ -0,0 +1,254 @@
// Copyright (c) 2014-2016 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef CACHEMULTIMAP_H_
#define CACHEMULTIMAP_H_
#include <cstddef>
#include <map>
#include <list>
#include <set>
#include "serialize.h"
#include "cachemap.h"
/**
* Map like container that keeps the N most recently added items
*/
template<typename K, typename V, typename Size = uint32_t>
class CacheMultiMap
{
public:
typedef Size size_type;
typedef CacheItem<K,V> item_t;
typedef std::list<item_t> list_t;
typedef typename list_t::iterator list_it;
typedef typename list_t::const_iterator list_cit;
typedef std::map<V,list_it> it_map_t;
typedef typename it_map_t::iterator it_map_it;
typedef typename it_map_t::const_iterator it_map_cit;
typedef std::map<K, it_map_t> map_t;
typedef typename map_t::iterator map_it;
typedef typename map_t::const_iterator map_cit;
private:
size_type nMaxSize;
size_type nCurrentSize;
list_t listItems;
map_t mapIndex;
public:
CacheMultiMap(size_type nMaxSizeIn = 0)
: nMaxSize(nMaxSizeIn),
nCurrentSize(0),
listItems(),
mapIndex()
{}
CacheMultiMap(const CacheMap<K,V>& other)
: nMaxSize(other.nMaxSize),
nCurrentSize(other.nCurrentSize),
listItems(other.listItems),
mapIndex()
{
RebuildIndex();
}
void Clear()
{
mapIndex.clear();
listItems.clear();
nCurrentSize = 0;
}
void SetMaxSize(size_type nMaxSizeIn)
{
nMaxSize = nMaxSizeIn;
}
size_type GetMaxSize() const {
return nMaxSize;
}
size_type GetSize() const {
return nCurrentSize;
}
void Insert(const K& key, const V& value)
{
if(nCurrentSize == nMaxSize) {
PruneLast();
}
map_it mit = mapIndex.find(key);
if(mit == mapIndex.end()) {
mit = mapIndex.insert(std::pair<K,it_map_t>(key, it_map_t())).first;
}
it_map_t& mapIt = mit->second;
if(mapIt.count(value) > 0) {
// Don't insert duplicates
return;
}
listItems.push_front(item_t(key, value));
list_it lit = listItems.begin();
mapIt[value] = lit;
++nCurrentSize;
}
bool HasKey(const K& key) const
{
map_cit it = mapIndex.find(key);
return (it != mapIndex.end());
}
bool Get(const K& key, V& value) const
{
map_cit it = mapIndex.find(key);
if(it == mapIndex.end()) {
return false;
}
const it_map_t& mapIt = it->second;
const item_t& item = *(mapIt.begin()->second);
value = item.value;
return true;
}
bool GetAll(const K& key, std::vector<V>& vecValues)
{
map_cit mit = mapIndex.find(key);
if(mit == mapIndex.end()) {
return false;
}
const it_map_t& mapIt = mit->second;
for(it_map_cit it = mapIt.begin(); it != mapIt.end(); ++it) {
const item_t& item = *(it->second);
vecValues.push_back(item.value);
}
return true;
}
void Erase(const K& key)
{
map_it mit = mapIndex.find(key);
if(mit == mapIndex.end()) {
return;
}
it_map_t& mapIt = mit->second;
for(it_map_it it = mapIt.begin(); it != mapIt.end(); ++it) {
listItems.erase(it->second);
--nCurrentSize;
}
mapIndex.erase(mit);
}
void Erase(const K& key, const V& value)
{
map_it mit = mapIndex.find(key);
if(mit == mapIndex.end()) {
return;
}
it_map_t& mapIt = mit->second;
it_map_it it = mapIt.find(value);
if(it == mapIt.end()) {
return;
}
listItems.erase(it->second);
--nCurrentSize;
mapIt.erase(it);
if(mapIt.size() < 1) {
mapIndex.erase(mit);
}
}
const list_t& GetItemList() const {
return listItems;
}
CacheMap<K,V>& operator=(const CacheMap<K,V>& other)
{
nMaxSize = other.nMaxSize;
nCurrentSize = other.nCurrentSize;
listItems = other.listItems;
RebuildIndex();
return *this;
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
{
READWRITE(nMaxSize);
READWRITE(nCurrentSize);
READWRITE(listItems);
if(ser_action.ForRead()) {
RebuildIndex();
}
}
private:
void PruneLast()
{
if(nCurrentSize < 1) {
return;
}
list_it lit = listItems.end();
--lit;
item_t& item = *lit;
map_it mit = mapIndex.find(item.key);
if(mit != mapIndex.end()) {
it_map_t& mapIt = mit->second;
mapIt.erase(item.value);
if(mapIt.size() < 1) {
mapIndex.erase(item.key);
}
}
listItems.pop_back();
--nCurrentSize;
}
void RebuildIndex()
{
mapIndex.clear();
for(list_it lit = listItems.begin(); lit != listItems.end(); ++lit) {
item_t& item = *lit;
map_it mit = mapIndex.find(item.key);
if(mit == mapIndex.end()) {
mit = mapIndex.insert(std::pair<K,it_map_t>(item.key, it_map_t())).first;
}
it_map_t& mapIt = mit->second;
mapIt[item.value] = lit;
}
}
};
#endif /* CACHEMULTIMAP_H_ */

View File

@ -346,7 +346,7 @@ bool CSuperblockManager::IsSuperblockTriggered(int nBlockHeight)
// MAKE SURE THIS TRIGGER IS ACTIVE VIA FUNDING CACHE FLAG // MAKE SURE THIS TRIGGER IS ACTIVE VIA FUNDING CACHE FLAG
if(pObj->fCachedFunding) { if(pObj->IsSetCachedFunding()) {
LogPrint("gobject", "CSuperblockManager::IsSuperblockTriggered -- fCacheFunding = true, returning true\n"); LogPrint("gobject", "CSuperblockManager::IsSuperblockTriggered -- fCacheFunding = true, returning true\n");
DBG( cout << "IsSuperblockTriggered returning true" << endl; ); DBG( cout << "IsSuperblockTriggered returning true" << endl; );
return true; return true;
@ -506,7 +506,7 @@ CSuperblock(uint256& nHash)
DBG( cout << "CSuperblock Constructor pGovObj : " DBG( cout << "CSuperblock Constructor pGovObj : "
<< pGovObj->GetDataAsString() << pGovObj->GetDataAsString()
<< ", nObjectType = " << pGovObj->nObjectType << ", nObjectType = " << pGovObj->GetObjectType()
<< endl; ); << endl; );
if (pGovObj->GetObjectType() != GOVERNANCE_OBJECT_TRIGGER) { if (pGovObj->GetObjectType() != GOVERNANCE_OBJECT_TRIGGER) {
@ -678,7 +678,7 @@ bool CSuperblock::IsValid(const CTransaction& txNew, int nBlockHeight, CAmount b
int nMinerPayments = nOutputs - nPayments; int nMinerPayments = nOutputs - nPayments;
LogPrint("gobject", "CSuperblock::IsValid nOutputs = %d, nPayments = %d, strData = %s\n", LogPrint("gobject", "CSuperblock::IsValid nOutputs = %d, nPayments = %d, strData = %s\n",
nOutputs, nPayments, GetGovernanceObject()->strData); nOutputs, nPayments, GetGovernanceObject()->GetDataAsHex());
// We require an exact match (including order) between the expected // We require an exact match (including order) between the expected
// superblock payments and the payments actually in the block, after // superblock payments and the payments actually in the block, after

View File

@ -1,8 +1,8 @@
// Copyright (c) 2014-2016 The Dash Core developers // Copyright (c) 2014-2016 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying // Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef GOVERANCE_CLASSES_H #ifndef GOVERNANCE_CLASSES_H
#define GOVERANCE_CLASSES_H #define GOVERNANCE_CLASSES_H
//#define ENABLE_DASH_DEBUG //#define ENABLE_DASH_DEBUG

View File

@ -0,0 +1,98 @@
// Copyright (c) 2014-2016 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef GOVERNANCE_EXCEPTIONS_H
#define GOVERNANCE_EXCEPTIONS_H
#include <exception>
#include <string>
#include <iostream>
enum governance_exception_type_enum_t {
/// Default value, normally indicates no exception condition occurred
GOVERNANCE_EXCEPTION_NONE = 0,
/// Unusual condition requiring no caller action
GOVERNANCE_EXCEPTION_WARNING = 1,
/// Requested operation cannot be performed
GOVERNANCE_EXCEPTION_PERMANENT_ERROR = 2,
/// Requested operation not currently possible, may resubmit later
GOVERNANCE_EXCEPTION_TEMPORARY_ERROR = 3,
/// Unexpected error (ie. should not happen unless there is a bug in the code)
GOVERNANCE_EXCEPTION_INTERNAL_ERROR = 4
};
inline std::ostream& operator<<(std::ostream& os, governance_exception_type_enum_t eType)
{
switch(eType) {
case GOVERNANCE_EXCEPTION_NONE:
os << "GOVERNANCE_EXCEPTION_NONE";
break;
case GOVERNANCE_EXCEPTION_WARNING:
os << "GOVERNANCE_EXCEPTION_WARNING";
break;
case GOVERNANCE_EXCEPTION_PERMANENT_ERROR:
os << "GOVERNANCE_EXCEPTION_PERMANENT_ERROR";
break;
case GOVERNANCE_EXCEPTION_TEMPORARY_ERROR:
os << "GOVERNANCE_EXCEPTION_TEMPORARY_ERROR";
break;
case GOVERNANCE_EXCEPTION_INTERNAL_ERROR:
os << "GOVERNANCE_EXCEPTION_INTERNAL_ERROR";
break;
}
return os;
}
/**
* A class which encapsulates information about a governance exception condition
*
* Derives from std::exception so is suitable for throwing
* (ie. will be caught by a std::exception handler) but may also be used as a
* normal object.
*/
class CGovernanceException : public std::exception
{
private:
std::string strMessage;
governance_exception_type_enum_t eType;
int nNodePenalty;
public:
CGovernanceException(const std::string& strMessageIn = "",
governance_exception_type_enum_t eTypeIn = GOVERNANCE_EXCEPTION_NONE,
int nNodePenaltyIn = 0)
: strMessage(),
eType(eTypeIn),
nNodePenalty(nNodePenaltyIn)
{
std::ostringstream ostr;
ostr << eType << ":" << strMessageIn;
strMessage = ostr.str();
}
virtual ~CGovernanceException() throw() {}
virtual const char* what() const throw()
{
return strMessage.c_str();
}
const std::string& GetMessage() const
{
return strMessage;
}
governance_exception_type_enum_t GetType() const
{
return eType;
}
int GetNodePenalty() const {
return nNodePenalty;
}
};
#endif

View File

@ -2,8 +2,8 @@
// Distributed under the MIT/X11 software license, see the accompanying // Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef GOVERANCE_MISC_H #ifndef GOVERNANCE_MISC_H
#define GOVERANCE_MISC_H #define GOVERNANCE_MISC_H
#include "main.h" #include "main.h"
#include "governance.h" #include "governance.h"
@ -51,4 +51,4 @@ class CGovernanceVote;
// } // }
// }; // };
#endif #endif

View File

@ -233,7 +233,7 @@ CGovernanceVote::CGovernanceVote(CTxIn vinMasternodeIn, uint256 nParentHashIn, v
vchSig() vchSig()
{} {}
void CGovernanceVote::Relay() void CGovernanceVote::Relay() const
{ {
CInv inv(MSG_GOVERNANCE_OBJECT_VOTE, GetHash()); CInv inv(MSG_GOVERNANCE_OBJECT_VOTE, GetHash());
RelayInv(inv, PROTOCOL_VERSION); RelayInv(inv, PROTOCOL_VERSION);
@ -262,15 +262,15 @@ bool CGovernanceVote::Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode)
return true; return true;
} }
bool CGovernanceVote::IsValid(bool fSignatureCheck) bool CGovernanceVote::IsValid(bool fSignatureCheck) const
{ {
if(nTime > GetTime() + (60*60)){ if(nTime > GetTime() + (60*60)) {
LogPrint("gobject", "CGovernanceVote::IsValid -- vote is too far ahead of current time - %s - nTime %lli - Max Time %lli\n", GetHash().ToString(), nTime, GetTime() + (60*60)); LogPrint("gobject", "CGovernanceVote::IsValid -- vote is too far ahead of current time - %s - nTime %lli - Max Time %lli\n", GetHash().ToString(), nTime, GetTime() + (60*60));
return false; return false;
} }
// support up to 50 actions (implemented in sentinel) // support up to 50 actions (implemented in sentinel)
if(nVoteSignal > 50) if(nVoteSignal > MAX_SUPPORTED_VOTE_SIGNAL)
{ {
LogPrint("gobject", "CGovernanceVote::IsValid -- Client attempted to vote on invalid signal(%d) - %s\n", nVoteSignal, GetHash().ToString()); LogPrint("gobject", "CGovernanceVote::IsValid -- Client attempted to vote on invalid signal(%d) - %s\n", nVoteSignal, GetHash().ToString());
return false; return false;
@ -302,3 +302,44 @@ bool CGovernanceVote::IsValid(bool fSignatureCheck)
return true; return true;
} }
bool operator==(const CGovernanceVote& vote1, const CGovernanceVote& vote2)
{
bool fResult = ((vote1.vinMasternode == vote2.vinMasternode) &&
(vote1.nParentHash == vote2.nParentHash) &&
(vote1.nVoteOutcome == vote2.nVoteOutcome) &&
(vote1.nVoteSignal == vote2.nVoteSignal) &&
(vote1.nTime == vote2.nTime));
return fResult;
}
bool operator<(const CGovernanceVote& vote1, const CGovernanceVote& vote2)
{
bool fResult = (vote1.vinMasternode < vote2.vinMasternode);
if(!fResult) {
return false;
}
fResult = (vote1.vinMasternode == vote2.vinMasternode);
fResult = fResult && (vote1.nParentHash < vote2.nParentHash);
if(!fResult) {
return false;
}
fResult = fResult && (vote1.nParentHash == vote2.nParentHash);
fResult = fResult && (vote1.nVoteOutcome < vote2.nVoteOutcome);
if(!fResult) {
return false;
}
fResult = fResult && (vote1.nVoteOutcome == vote2.nVoteOutcome);
fResult = fResult && (vote1.nVoteSignal == vote2.nVoteSignal);
if(!fResult) {
return false;
}
fResult = fResult && (vote1.nVoteSignal == vote2.nVoteSignal);
fResult = fResult && (vote1.nTime < vote2.nTime);
return fResult;
}

View File

@ -2,8 +2,8 @@
// Distributed under the MIT/X11 software license, see the accompanying // Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef GOVERANCE_VOTE_H #ifndef GOVERNANCE_VOTE_H
#define GOVERANCE_VOTE_H #define GOVERNANCE_VOTE_H
#include "main.h" #include "main.h"
#include "sync.h" #include "sync.h"
@ -68,6 +68,8 @@ enum vote_signal_enum_t {
VOTE_SIGNAL_CUSTOM20 = 35 VOTE_SIGNAL_CUSTOM20 = 35
}; };
static const int MAX_SUPPORTED_VOTE_SIGNAL = VOTE_SIGNAL_ENDORSED;
/** /**
* Governance Voting * Governance Voting
* *
@ -89,6 +91,10 @@ public:
class CGovernanceVote class CGovernanceVote
{ {
friend bool operator==(const CGovernanceVote& vote1, const CGovernanceVote& vote2);
friend bool operator<(const CGovernanceVote& vote1, const CGovernanceVote& vote2);
private: private:
bool fValid; //if the vote is currently valid / counted bool fValid; //if the vote is currently valid / counted
bool fSynced; //if we've sent this to our peers bool fSynced; //if we've sent this to our peers
@ -120,8 +126,12 @@ public:
void SetSignature(const std::vector<unsigned char>& vchSigIn) { vchSig = vchSigIn; } void SetSignature(const std::vector<unsigned char>& vchSigIn) { vchSig = vchSigIn; }
bool Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode); bool Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode);
bool IsValid(bool fSignatureCheck); bool IsValid(bool fSignatureCheck) const;
void Relay(); void Relay() const;
std::string GetVoteString() const {
return CGovernanceVoting::ConvertOutcomeToString(GetOutcome());
}
CTxIn& GetVinMasternode() { return vinMasternode; } CTxIn& GetVinMasternode() { return vinMasternode; }
@ -144,7 +154,7 @@ public:
return ss.GetHash(); return ss.GetHash();
} }
std::string ToString() std::string ToString() const
{ {
std::ostringstream ostr; std::ostringstream ostr;
ostr << vinMasternode.ToString() << ":" ostr << vinMasternode.ToString() << ":"
@ -197,6 +207,7 @@ public:
}; };
/** /**
* 12.1.1 - CGovernanceVoteManager * 12.1.1 - CGovernanceVoteManager
* ------------------------------- * -------------------------------

84
src/governance-votedb.cpp Normal file
View File

@ -0,0 +1,84 @@
// Copyright (c) 2014-2016 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "governance-votedb.h"
CGovernanceObjectVoteFile::CGovernanceObjectVoteFile()
: nMemoryVotes(0),
listVotes(),
mapVoteIndex()
{}
CGovernanceObjectVoteFile::CGovernanceObjectVoteFile(const CGovernanceObjectVoteFile& other)
: nMemoryVotes(other.nMemoryVotes),
listVotes(other.listVotes),
mapVoteIndex()
{
RebuildIndex();
}
void CGovernanceObjectVoteFile::AddVote(const CGovernanceVote& vote)
{
listVotes.push_front(vote);
mapVoteIndex[vote.GetHash()] = listVotes.begin();
++nMemoryVotes;
}
bool CGovernanceObjectVoteFile::HasVote(const uint256& nHash) const
{
vote_m_cit it = mapVoteIndex.find(nHash);
if(it == mapVoteIndex.end()) {
return false;
}
return true;
}
bool CGovernanceObjectVoteFile::GetVote(const uint256& nHash, CGovernanceVote& vote) const
{
vote_m_cit it = mapVoteIndex.find(nHash);
if(it == mapVoteIndex.end()) {
return false;
}
vote = *(it->second);
return true;
}
std::vector<CGovernanceVote> CGovernanceObjectVoteFile::GetVotes() const
{
std::vector<CGovernanceVote> vecResult;
for(vote_l_cit it = listVotes.begin(); it != listVotes.end(); ++it) {
vecResult.push_back(*it);
}
return vecResult;
}
void CGovernanceObjectVoteFile::RemoveVotesFromMasternode(const CTxIn& vinMasternode)
{
vote_l_it it = listVotes.begin();
while(it != listVotes.end()) {
if(it->GetVinMasternode() == vinMasternode) {
listVotes.erase(it++);
}
else {
++it;
}
}
}
CGovernanceObjectVoteFile& CGovernanceObjectVoteFile::operator=(const CGovernanceObjectVoteFile& other)
{
nMemoryVotes = other.nMemoryVotes;
listVotes = other.listVotes;
RebuildIndex();
return *this;
}
void CGovernanceObjectVoteFile::RebuildIndex()
{
mapVoteIndex.clear();
for(vote_l_it it = listVotes.begin(); it != listVotes.end(); ++it) {
CGovernanceVote& vote = *it;
mapVoteIndex[vote.GetHash()] = it;
}
}

93
src/governance-votedb.h Normal file
View File

@ -0,0 +1,93 @@
// Copyright (c) 2014-2016 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef GOVERNANCE_VOTEDB_H
#define GOVERNANCE_VOTEDB_H
#include <list>
#include <map>
#include "governance-vote.h"
#include "serialize.h"
#include "uint256.h"
/**
* Represents the collection of votes associated with a given CGovernanceObject
* Recently received votes are held in memory until a maximum size is reached after
* which older votes a flushed to a disk file.
*
* Note: This is a stub implementation that doesn't limit the number of votes held
* in memory and doesn't flush to disk.
*/
class CGovernanceObjectVoteFile
{
public: // Types
typedef std::list<CGovernanceVote> vote_l_t;
typedef vote_l_t::iterator vote_l_it;
typedef vote_l_t::const_iterator vote_l_cit;
typedef std::map<uint256,vote_l_it> vote_m_t;
typedef vote_m_t::iterator vote_m_it;
typedef vote_m_t::const_iterator vote_m_cit;
private:
static const int MAX_MEMORY_VOTES = -1;
int nMemoryVotes;
vote_l_t listVotes;
vote_m_t mapVoteIndex;
public:
CGovernanceObjectVoteFile();
CGovernanceObjectVoteFile(const CGovernanceObjectVoteFile& other);
/**
* Add a vote to the file
*/
void AddVote(const CGovernanceVote& vote);
/**
* Return true if the vote with this hash is currently cached in memory
*/
bool HasVote(const uint256& nHash) const;
/**
* Retrieve a vote cached in memory
*/
bool GetVote(const uint256& nHash, CGovernanceVote& vote) const;
int GetVoteCount() {
return nMemoryVotes;
}
std::vector<CGovernanceVote> GetVotes() const;
CGovernanceObjectVoteFile& operator=(const CGovernanceObjectVoteFile& other);
void RemoveVotesFromMasternode(const CTxIn& vinMasternode);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
{
READWRITE(nMemoryVotes);
READWRITE(listVotes);
if(ser_action.ForRead()) {
RebuildIndex();
}
}
private:
void RebuildIndex();
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -2,8 +2,8 @@
// Distributed under the MIT/X11 software license, see the accompanying // Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef GOVERANCE_H #ifndef GOVERNANCE_H
#define GOVERANCE_H #define GOVERNANCE_H
//#define ENABLE_DASH_DEBUG //#define ENABLE_DASH_DEBUG
@ -15,17 +15,22 @@
#include "util.h" #include "util.h"
#include "base58.h" #include "base58.h"
#include "masternode.h" #include "masternode.h"
#include "governance-exceptions.h"
#include "governance-vote.h" #include "governance-vote.h"
#include "governance-votedb.h"
#include "masternodeman.h" #include "masternodeman.h"
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "init.h" #include "init.h"
#include <univalue.h> #include <univalue.h>
#include "utilstrencodings.h" #include "utilstrencodings.h"
#include "cachemap.h"
#include "cachemultimap.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
class CGovernanceManager; class CGovernanceManager;
class CGovernanceTriggerManager;
class CGovernanceObject; class CGovernanceObject;
class CGovernanceVote; class CGovernanceVote;
@ -57,6 +62,8 @@ extern CGovernanceManager governance;
// //
class CGovernanceManager class CGovernanceManager
{ {
friend class CGovernanceObject;
public: // Types public: // Types
typedef std::map<uint256, CGovernanceObject> object_m_t; typedef std::map<uint256, CGovernanceObject> object_m_t;
@ -65,6 +72,8 @@ public: // Types
typedef object_m_t::const_iterator object_m_cit; typedef object_m_t::const_iterator object_m_cit;
typedef CacheMap<uint256, CGovernanceObject*> object_ref_cache_t;
typedef std::map<uint256, int> count_m_t; typedef std::map<uint256, int> count_m_t;
typedef count_m_t::iterator count_m_it; typedef count_m_t::iterator count_m_it;
@ -77,11 +86,9 @@ public: // Types
typedef vote_m_t::const_iterator vote_m_cit; typedef vote_m_t::const_iterator vote_m_cit;
typedef std::map<uint256, CTransaction> transaction_m_t; typedef CacheMap<uint256, CGovernanceVote> vote_cache_t;
typedef transaction_m_t::iterator transaction_m_it; typedef CacheMultiMap<uint256, CGovernanceVote> vote_mcache_t;
typedef transaction_m_t::const_iterator transaction_m_cit;
typedef object_m_t::size_type size_type; typedef object_m_t::size_type size_type;
@ -91,10 +98,17 @@ public: // Types
typedef txout_m_t::const_iterator txout_m_cit; typedef txout_m_t::const_iterator txout_m_cit;
private: typedef std::set<uint256> hash_s_t;
typedef hash_s_t::iterator hash_s_it;
typedef hash_s_t::const_iterator hash_s_cit;
private:
static const int MAX_CACHE_SIZE = 1000000;
static const std::string SERIALIZATION_VERSION_STRING;
//hold txes until they mature enough to use
transaction_m_t mapCollateral;
// Keep track of current block index // Keep track of current block index
const CBlockIndex *pCurrentBlockIndex; const CBlockIndex *pCurrentBlockIndex;
@ -105,57 +119,68 @@ private:
object_m_t mapObjects; object_m_t mapObjects;
count_m_t mapSeenGovernanceObjects; count_m_t mapSeenGovernanceObjects;
count_m_t mapSeenVotes;
vote_m_t mapOrphanVotes;
// todo: one of these should point to the other object_ref_cache_t mapVoteToObject;
// -- must be carefully managed while adding/removing/updating
vote_m_t mapVotesByHash; vote_cache_t mapInvalidVotes;
vote_m_t mapVotesByType;
vote_mcache_t mapOrphanVotes;
txout_m_t mapLastMasternodeTrigger; txout_m_t mapLastMasternodeTrigger;
hash_s_t setRequestedObjects;
hash_s_t setRequestedVotes;
public: public:
// critical section to protect the inner data structures // critical section to protect the inner data structures
mutable CCriticalSection cs; mutable CCriticalSection cs;
CGovernanceManager(); CGovernanceManager();
virtual ~CGovernanceManager() {}
void ClearSeen() void ClearSeen()
{ {
LOCK(cs); LOCK(cs);
mapSeenGovernanceObjects.clear(); mapSeenGovernanceObjects.clear();
mapSeenVotes.clear();
} }
int CountProposalInventoryItems() int CountProposalInventoryItems()
{ {
return mapSeenGovernanceObjects.size() + mapSeenVotes.size(); // TODO What is this for ?
return mapSeenGovernanceObjects.size();
//return mapSeenGovernanceObjects.size() + mapSeenVotes.size();
} }
/**
* This is called by AlreadyHave in main.cpp as part of the inventory
* retrieval process. Returns true if we want to retrieve the object, otherwise
* false. (Note logic is inverted in AlreadyHave).
*/
bool ConfirmInventoryRequest(const CInv& inv);
void Sync(CNode* node, uint256 nProp); void Sync(CNode* node, uint256 nProp);
void SyncParentObjectByVote(CNode* pfrom, const CGovernanceVote& vote); void SyncParentObjectByVote(CNode* pfrom, const CGovernanceVote& vote);
void ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); void ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv);
void NewBlock(); void NewBlock();
CGovernanceObject *FindGovernanceObject(const uint256& nHash); CGovernanceObject *FindGovernanceObject(const uint256& nHash);
std::vector<CGovernanceVote*> GetMatchingVotes(const uint256& nParentHash); std::vector<CGovernanceVote> GetMatchingVotes(const uint256& nParentHash);
std::vector<CGovernanceObject*> GetAllNewerThan(int64_t nMoreThanTime); std::vector<CGovernanceObject*> GetAllNewerThan(int64_t nMoreThanTime);
int CountMatchingVotes(CGovernanceObject& govobj, vote_signal_enum_t nVoteSignalIn, vote_outcome_enum_t nVoteOutcomeIn);
bool IsBudgetPaymentBlock(int nBlockHeight); bool IsBudgetPaymentBlock(int nBlockHeight);
bool AddGovernanceObject (CGovernanceObject& govobj); bool AddGovernanceObject (CGovernanceObject& govobj);
bool AddOrUpdateVote(const CGovernanceVote& vote, CNode* pfrom, std::string& strError);
std::string GetRequiredPaymentsString(int nBlockHeight); std::string GetRequiredPaymentsString(int nBlockHeight);
void CleanAndRemove(bool fSignatureCheck);
void UpdateCachesAndClean();
void CheckAndRemove() {UpdateCachesAndClean();}
void CheckOrphanVotes(); void UpdateCachesAndClean();
void CheckAndRemove() {UpdateCachesAndClean();}
void Clear() void Clear()
{ {
@ -164,10 +189,9 @@ public:
LogPrint("gobject", "Governance object manager was cleared\n"); LogPrint("gobject", "Governance object manager was cleared\n");
mapObjects.clear(); mapObjects.clear();
mapSeenGovernanceObjects.clear(); mapSeenGovernanceObjects.clear();
mapSeenVotes.clear(); mapVoteToObject.Clear();
mapOrphanVotes.clear(); mapInvalidVotes.Clear();
mapVotesByType.clear(); mapOrphanVotes.Clear();
mapVotesByHash.clear();
mapLastMasternodeTrigger.clear(); mapLastMasternodeTrigger.clear();
} }
@ -178,14 +202,25 @@ public:
template <typename Stream, typename Operation> template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
LOCK(cs); LOCK(cs);
std::string strVersion;
if(ser_action.ForRead()) {
READWRITE(strVersion);
}
else {
strVersion = SERIALIZATION_VERSION_STRING;
READWRITE(strVersion);
}
READWRITE(mapSeenGovernanceObjects); READWRITE(mapSeenGovernanceObjects);
READWRITE(mapSeenVotes); READWRITE(mapInvalidVotes);
READWRITE(mapOrphanVotes); READWRITE(mapOrphanVotes);
READWRITE(mapObjects); READWRITE(mapObjects);
READWRITE(mapVotesByHash);
READWRITE(mapVotesByType);
READWRITE(mapLastMasternodeTrigger); READWRITE(mapLastMasternodeTrigger);
if(ser_action.ForRead() && (strVersion != SERIALIZATION_VERSION_STRING)) {
Clear();
return;
}
if(ser_action.ForRead()) { if(ser_action.ForRead()) {
RebuildIndexes();
AddCachedTriggers(); AddCachedTriggers();
} }
} }
@ -211,11 +246,90 @@ public:
bool MasternodeRateCheck(const CTxIn& vin, int nObjectType); bool MasternodeRateCheck(const CTxIn& vin, int nObjectType);
bool ProcessVote(const CGovernanceVote& vote, CGovernanceException& exception) {
return ProcessVote(NULL, vote, exception);
}
void CheckMasternodeOrphanVotes();
private: private:
void RequestGovernanceObject(CNode* pfrom, const uint256& nHash);
void AddInvalidVote(const CGovernanceVote& vote)
{
mapInvalidVotes.Insert(vote.GetHash(), vote);
}
void AddOrphanVote(const CGovernanceVote& vote)
{
mapOrphanVotes.Insert(vote.GetHash(), vote);
}
bool ProcessVote(CNode* pfrom, const CGovernanceVote& vote, CGovernanceException& exception);
/// Called to indicate a requested object has been received
bool AcceptObjectMessage(const uint256& nHash);
/// Called to indicate a requested vote has been received
bool AcceptVoteMessage(const uint256& nHash);
static bool AcceptMessage(const uint256& nHash, hash_s_t& setHash);
void CheckOrphanVotes(CNode* pfrom, CGovernanceObject& govobj, CGovernanceException& exception);
void RebuildIndexes();
/// Returns MN index, handling the case of index rebuilds
int GetMasternodeIndex(const CTxIn& masternodeVin);
void RebuildVoteMaps();
void AddCachedTriggers(); void AddCachedTriggers();
}; };
struct vote_instance_t {
vote_outcome_enum_t eOutcome;
int64_t nTime;
vote_instance_t(vote_outcome_enum_t eOutcomeIn = VOTE_OUTCOME_NONE, int64_t nTimeIn = 0)
: eOutcome(eOutcomeIn),
nTime(nTimeIn)
{}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
{
int nOutcome = int(eOutcome);
READWRITE(nOutcome);
READWRITE(nTime);
if(ser_action.ForRead()) {
eOutcome = vote_outcome_enum_t(nOutcome);
}
}
};
typedef std::map<int,vote_instance_t> vote_instance_m_t;
typedef vote_instance_m_t::iterator vote_instance_m_it;
typedef vote_instance_m_t::const_iterator vote_instance_m_cit;
struct vote_rec_t {
vote_instance_m_t mapInstances;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
{
READWRITE(mapInstances);
}
};
/** /**
* Governance Object * Governance Object
* *
@ -223,41 +337,140 @@ private:
class CGovernanceObject class CGovernanceObject
{ {
friend class CGovernanceManager;
friend class CGovernanceTriggerManager;
public: // Types
typedef std::map<int, vote_rec_t> vote_m_t;
typedef vote_m_t::iterator vote_m_it;
typedef vote_m_t::const_iterator vote_m_cit;
typedef CacheMultiMap<CTxIn, CGovernanceVote> vote_mcache_t;
private: private:
// critical section to protect the inner data structures /// critical section to protect the inner data structures
mutable CCriticalSection cs; mutable CCriticalSection cs;
public: /// Object typecode
uint256 nHashParent; //parent object, 0 is root
int nRevision; //object revision in the system
int64_t nTime; //time this object was created
uint256 nCollateralHash; //fee-tx
std::string strData; // Data field - can be used for anything
int nObjectType; int nObjectType;
// Masternode info for signed objects /// parent object, 0 is root
uint256 nHashParent;
/// object revision in the system
int nRevision;
/// time this object was created
int64_t nTime;
/// fee-tx
uint256 nCollateralHash;
/// Data field - can be used for anything
std::string strData;
/// Masternode info for signed objects
CTxIn vinMasternode; CTxIn vinMasternode;
std::vector<unsigned char> vchSig; std::vector<unsigned char> vchSig;
bool fCachedLocalValidity; // is valid by blockchain /// is valid by blockchain
bool fCachedLocalValidity;
std::string strLocalValidityError; std::string strLocalValidityError;
// VARIOUS FLAGS FOR OBJECT / SET VIA MASTERNODE VOTING // VARIOUS FLAGS FOR OBJECT / SET VIA MASTERNODE VOTING
bool fCachedFunding; // true == minimum network support has been reached for this object to be funded (doesn't mean it will for sure though) /// true == minimum network support has been reached for this object to be funded (doesn't mean it will for sure though)
bool fCachedValid; // true == minimum network has been reached flagging this object as a valid and understood goverance object (e.g, the serialized data is correct format, etc) bool fCachedFunding;
bool fCachedDelete; // true == minimum network support has been reached saying this object should be deleted from the system entirely
bool fCachedEndorsed; // true == minimum network support has been reached flagging this object as endorsed by an elected representative body (e.g. business review board / technecial review board /etc)
bool fDirtyCache; // object was updated and cached values should be updated soon
bool fUnparsable; // data field was unparsible, object will be rejected
bool fExpired; // Object is no longer of interest
/// true == minimum network has been reached flagging this object as a valid and understood goverance object (e.g, the serialized data is correct format, etc)
bool fCachedValid;
/// true == minimum network support has been reached saying this object should be deleted from the system entirely
bool fCachedDelete;
/** true == minimum network support has been reached flagging this object as endorsed by an elected representative body
* (e.g. business review board / technecial review board /etc)
*/
bool fCachedEndorsed;
/// object was updated and cached values should be updated soon
bool fDirtyCache;
/// Object is no longer of interest
bool fExpired;
/// Failed to parse object data
bool fUnparsable;
vote_m_t mapCurrentMNVotes;
/// Limited map of votes orphaned by MN
vote_mcache_t mapOrphanVotes;
CGovernanceObjectVoteFile fileVotes;
public:
CGovernanceObject(); CGovernanceObject();
CGovernanceObject(uint256 nHashParentIn, int nRevisionIn, int64_t nTime, uint256 nCollateralHashIn, std::string strDataIn); CGovernanceObject(uint256 nHashParentIn, int nRevisionIn, int64_t nTime, uint256 nCollateralHashIn, std::string strDataIn);
CGovernanceObject(const CGovernanceObject& other); CGovernanceObject(const CGovernanceObject& other);
void swap(CGovernanceObject& first, CGovernanceObject& second); // nothrow void swap(CGovernanceObject& first, CGovernanceObject& second); // nothrow
// Public Getter methods
int64_t GetCreationTime() const {
return nTime;
}
int GetObjectType() const {
return nObjectType;
}
const uint256& GetCollateralHash() const {
return nCollateralHash;
}
const CTxIn& GetMasternodeVin() const {
return vinMasternode;
}
bool IsSetCachedFunding() const {
return fCachedFunding;
}
bool IsSetCachedValid() const {
return fCachedValid;
}
bool IsSetCachedDelete() const {
return fCachedDelete;
}
bool IsSetCachedEndorsed() const {
return fCachedEndorsed;
}
bool IsSetDirtyCache() const {
return fDirtyCache;
}
bool IsSetExpired() const {
return fExpired;
}
void InvalidateVoteCache() {
fDirtyCache = true;
}
CGovernanceObjectVoteFile& GetVoteFile() {
return fileVotes;
}
// Signature related functions // Signature related functions
void SetMasternodeInfo(const CTxIn& vin); void SetMasternodeInfo(const CTxIn& vin);
@ -272,8 +485,9 @@ public:
bool IsCollateralValid(std::string& strError); bool IsCollateralValid(std::string& strError);
void UpdateLocalValidity(const CBlockIndex *pCurrentBlockIndex); void UpdateLocalValidity(const CBlockIndex *pCurrentBlockIndex);
void UpdateSentinelVariables(const CBlockIndex *pCurrentBlockIndex); void UpdateSentinelVariables(const CBlockIndex *pCurrentBlockIndex);
int GetObjectType();
int GetObjectSubtype(); int GetObjectSubtype();
CAmount GetMinCollateralFee(); CAmount GetMinCollateralFee();
@ -281,15 +495,18 @@ public:
UniValue GetJSONObject(); UniValue GetJSONObject();
void Relay(); void Relay();
uint256 GetHash(); uint256 GetHash();
// GET VOTE COUNT FOR SIGNAL // GET VOTE COUNT FOR SIGNAL
int GetAbsoluteYesCount(vote_signal_enum_t eVoteSignalIn); int CountMatchingVotes(vote_signal_enum_t eVoteSignalIn, vote_outcome_enum_t eVoteOutcomeIn) const;
int GetAbsoluteNoCount(vote_signal_enum_t eVoteSignalIn);
int GetYesCount(vote_signal_enum_t eVoteSignalIn); int GetAbsoluteYesCount(vote_signal_enum_t eVoteSignalIn) const;
int GetNoCount(vote_signal_enum_t eVoteSignalIn); int GetAbsoluteNoCount(vote_signal_enum_t eVoteSignalIn) const;
int GetAbstainCount(vote_signal_enum_t eVoteSignalIn); int GetYesCount(vote_signal_enum_t eVoteSignalIn) const;
int GetNoCount(vote_signal_enum_t eVoteSignalIn) const;
int GetAbstainCount(vote_signal_enum_t eVoteSignalIn) const;
// FUNCTIONS FOR DEALING WITH DATA STRING // FUNCTIONS FOR DEALING WITH DATA STRING
@ -313,16 +530,33 @@ public:
READWRITE(nObjectType); READWRITE(nObjectType);
READWRITE(vinMasternode); READWRITE(vinMasternode);
READWRITE(vchSig); READWRITE(vchSig);
if(nType & SER_DISK) {
// Only include these for the disk file format
LogPrint("gobject", "CGovernanceObject::SerializationOp Reading/writing votes from/to disk\n");
READWRITE(mapCurrentMNVotes);
READWRITE(fileVotes);
LogPrint("gobject", "CGovernanceObject::SerializationOp hash = %s, vote count = %d\n", GetHash().ToString(), fileVotes.GetVoteCount());
}
// AFTER DESERIALIZATION OCCURS, CACHED VARIABLES MUST BE CALCULATED MANUALLY // AFTER DESERIALIZATION OCCURS, CACHED VARIABLES MUST BE CALCULATED MANUALLY
} }
private: private:
// FUNCTIONS FOR DEALING WITH DATA STRING // FUNCTIONS FOR DEALING WITH DATA STRING
void LoadData(); void LoadData();
void GetData(UniValue& objResult); void GetData(UniValue& objResult);
bool ProcessVote(CNode* pfrom,
const CGovernanceVote& vote,
CGovernanceException& exception);
void RebuildVoteMap();
/// Called when MN's which have voted on this object have been removed
void ClearMasternodeVotes();
void CheckOrphanVotes();
}; };

View File

@ -4948,14 +4948,11 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
return mapDarksendBroadcastTxes.count(inv.hash); return mapDarksendBroadcastTxes.count(inv.hash);
case MSG_GOVERNANCE_OBJECT: case MSG_GOVERNANCE_OBJECT:
return governance.HaveObjectForHash(inv.hash);
case MSG_GOVERNANCE_OBJECT_VOTE: case MSG_GOVERNANCE_OBJECT_VOTE:
return governance.HaveVoteForHash(inv.hash); return ! governance.ConfirmInventoryRequest(inv);
case MSG_MASTERNODE_VERIFY: case MSG_MASTERNODE_VERIFY:
return mnodeman.mapSeenMasternodeVerification.count(inv.hash); return mnodeman.mapSeenMasternodeVerification.count(inv.hash);
} }
// Don't know what it is, just say we already got one // Don't know what it is, just say we already got one
@ -6720,9 +6717,15 @@ bool SendMessages(CNode* pto)
// //
// Message: getdata (non-blocks) // Message: getdata (non-blocks)
// //
int64_t nFirst = -1;
if(!pto->mapAskFor.empty()) {
nFirst = (*pto->mapAskFor.begin()).first;
}
LogPrint("net", "SendMessages (mapAskFor) -- before loop: nNow = %d, nFirst = %d\n", nNow, nFirst);
while (!pto->fDisconnect && !pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) while (!pto->fDisconnect && !pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow)
{ {
const CInv& inv = (*pto->mapAskFor.begin()).second; const CInv& inv = (*pto->mapAskFor.begin()).second;
LogPrint("net", "SendMessages (mapAskFor) -- inv = %s peer=%d\n", inv.ToString(), pto->id);
if (!AlreadyHave(inv)) if (!AlreadyHave(inv))
{ {
if (fDebug) if (fDebug)
@ -6735,6 +6738,7 @@ bool SendMessages(CNode* pto)
} }
} else { } else {
//If we're not going to ask, don't expect a response. //If we're not going to ask, don't expect a response.
LogPrint("net", "SendMessages -- already have inv = %s peer=%d\n", inv.ToString(), pto->id);
pto->setAskFor.erase(inv.hash); pto->setAskFor.erase(inv.hash);
} }
pto->mapAskFor.erase(pto->mapAskFor.begin()); pto->mapAskFor.erase(pto->mapAskFor.begin());

View File

@ -882,6 +882,14 @@ void CMasternode::UpdateWatchdogVoteTime()
*/ */
void CMasternode::FlagGovernanceItemsAsDirty() void CMasternode::FlagGovernanceItemsAsDirty()
{ {
std::map<uint256, int>::iterator it = mapGovernanceObjectsVotedOn.begin();
while(it != mapGovernanceObjectsVotedOn.end()){
CGovernanceObject *pObj = governance.FindGovernanceObject((*it).first);
if(pObj) pObj->InvalidateVoteCache();
++it;
}
std::vector<uint256> vecDirty; std::vector<uint256> vecDirty;
{ {
std::map<uint256, int>::iterator it = mapGovernanceObjectsVotedOn.begin(); std::map<uint256, int>::iterator it = mapGovernanceObjectsVotedOn.begin();

View File

@ -5,6 +5,7 @@
#include "masternodeman.h" #include "masternodeman.h"
#include "activemasternode.h" #include "activemasternode.h"
#include "darksend.h" #include "darksend.h"
#include "governance.h"
#include "masternode.h" #include "masternode.h"
#include "masternode-payments.h" #include "masternode-payments.h"
#include "masternode-sync.h" #include "masternode-sync.h"
@ -38,7 +39,51 @@ struct CompareScoreMN
} }
}; };
CMasternodeIndex::CMasternodeIndex()
: nSize(0),
mapIndex(),
mapReverseIndex()
{}
bool CMasternodeIndex::Get(int nIndex, CTxIn& vinMasternode) const
{
rindex_m_cit it = mapReverseIndex.find(nIndex);
if(it == mapReverseIndex.end()) {
return false;
}
vinMasternode = it->second;
return true;
}
int CMasternodeIndex::GetMasternodeIndex(const CTxIn& vinMasternode) const
{
index_m_cit it = mapIndex.find(vinMasternode);
if(it == mapIndex.end()) {
return -1;
}
return it->second;
}
void CMasternodeIndex::AddMasternodeVIN(const CTxIn& vinMasternode)
{
index_m_it it = mapIndex.find(vinMasternode);
if(it != mapIndex.end()) {
return;
}
int nNextIndex = nSize;
mapIndex[vinMasternode] = nNextIndex;
mapReverseIndex[nNextIndex] = vinMasternode;
++nSize;
}
void CMasternodeIndex::Clear()
{
mapIndex.clear();
mapReverseIndex.clear();
nSize = 0;
}
struct CompareByAddr struct CompareByAddr
{ {
bool operator()(const CMasternode* t1, bool operator()(const CMasternode* t1,
const CMasternode* t2) const const CMasternode* t2) const
@ -47,6 +92,33 @@ struct CompareByAddr
} }
}; };
void CMasternodeIndex::RebuildIndex()
{
nSize = mapIndex.size();
for(index_m_it it = mapIndex.begin(); it != mapIndex.end(); ++it) {
mapReverseIndex[it->second] = it->first;
}
}
CMasternodeMan::CMasternodeMan()
: cs(),
vMasternodes(),
mAskedUsForMasternodeList(),
mWeAskedForMasternodeList(),
mWeAskedForMasternodeListEntry(),
nLastIndexRebuildTime(0),
indexMasternodes(),
indexMasternodesOld(),
fIndexRebuilt(false),
fMasternodesAdded(false),
fMasternodesRemoved(false),
vecDirtyGovernanceObjectHashes(),
nLastWatchdogVoteTime(0),
mapSeenMasternodeBroadcast(),
mapSeenMasternodePing(),
nDsqCount(0)
{}
bool CMasternodeMan::Add(CMasternode &mn) bool CMasternodeMan::Add(CMasternode &mn)
{ {
LOCK(cs); LOCK(cs);
@ -59,13 +131,15 @@ bool CMasternodeMan::Add(CMasternode &mn)
LogPrint("masternode", "CMasternodeMan::Add -- Adding new Masternode: addr=%s, %i now\n", mn.addr.ToString(), size() + 1); LogPrint("masternode", "CMasternodeMan::Add -- Adding new Masternode: addr=%s, %i now\n", mn.addr.ToString(), size() + 1);
mn.nTimeLastWatchdogVote = mn.sigTime; mn.nTimeLastWatchdogVote = mn.sigTime;
vMasternodes.push_back(mn); vMasternodes.push_back(mn);
indexMasternodes.AddMasternodeVIN(mn.vin);
fMasternodesAdded = true;
return true; return true;
} }
return false; return false;
} }
void CMasternodeMan::AskForMN(CNode* pnode, CTxIn &vin) void CMasternodeMan::AskForMN(CNode* pnode, const CTxIn &vin)
{ {
if(!pnode) return; if(!pnode) return;
@ -99,106 +173,117 @@ void CMasternodeMan::CheckAndRemove(bool fForceExpiredRemoval)
Check(); Check();
LOCK(cs); {
LOCK(cs);
// Remove inactive and outdated masternodes // Remove inactive and outdated masternodes
std::vector<CMasternode>::iterator it = vMasternodes.begin(); std::vector<CMasternode>::iterator it = vMasternodes.begin();
while(it != vMasternodes.end()) { while(it != vMasternodes.end()) {
bool fRemove = // If it's marked to be removed from the list by CMasternode::Check for whatever reason ... bool fRemove = // If it's marked to be removed from the list by CMasternode::Check for whatever reason ...
(*it).nActiveState == CMasternode::MASTERNODE_REMOVE || (*it).nActiveState == CMasternode::MASTERNODE_REMOVE ||
// or collateral was spent ... // or collateral was spent ...
(*it).nActiveState == CMasternode::MASTERNODE_OUTPOINT_SPENT || (*it).nActiveState == CMasternode::MASTERNODE_OUTPOINT_SPENT ||
// or we were asked to remove exired entries ... // or we were asked to remove exired entries ...
(fForceExpiredRemoval && (*it).nActiveState == CMasternode::MASTERNODE_EXPIRED); (fForceExpiredRemoval && (*it).nActiveState == CMasternode::MASTERNODE_EXPIRED);
if (fRemove) { if (fRemove) {
LogPrint("masternode", "CMasternodeMan::CheckAndRemove -- Removing Masternode: %s addr=%s %i now\n", (*it).GetStatus(), (*it).addr.ToString(), size() - 1); LogPrint("masternode", "CMasternodeMan::CheckAndRemove -- Removing Masternode: %s addr=%s %i now\n", (*it).GetStatus(), (*it).addr.ToString(), size() - 1);
// erase all of the broadcasts we've seen from this txin, ... // erase all of the broadcasts we've seen from this txin, ...
mapSeenMasternodeBroadcast.erase(CMasternodeBroadcast(*it).GetHash()); mapSeenMasternodeBroadcast.erase(CMasternodeBroadcast(*it).GetHash());
// allow us to ask for this masternode again if we see another ping ... // allow us to ask for this masternode again if we see another ping ...
mWeAskedForMasternodeListEntry.erase((*it).vin.prevout); mWeAskedForMasternodeListEntry.erase((*it).vin.prevout);
// and finally remove it from the list // and finally remove it from the list
it = vMasternodes.erase(it); it = vMasternodes.erase(it);
} else { fMasternodesRemoved = true;
++it; } else {
++it;
}
}
// check who's asked for the Masternode list
std::map<CNetAddr, int64_t>::iterator it1 = mAskedUsForMasternodeList.begin();
while(it1 != mAskedUsForMasternodeList.end()){
if((*it1).second < GetTime()) {
mAskedUsForMasternodeList.erase(it1++);
} else {
++it1;
}
}
// check who we asked for the Masternode list
it1 = mWeAskedForMasternodeList.begin();
while(it1 != mWeAskedForMasternodeList.end()){
if((*it1).second < GetTime()){
mWeAskedForMasternodeList.erase(it1++);
} else {
++it1;
}
}
// check which Masternodes we've asked for
std::map<COutPoint, int64_t>::iterator it2 = mWeAskedForMasternodeListEntry.begin();
while(it2 != mWeAskedForMasternodeListEntry.end()){
if((*it2).second < GetTime()){
mWeAskedForMasternodeListEntry.erase(it2++);
} else {
++it2;
}
}
std::map<CNetAddr, CMasternodeVerification>::iterator itv1 = mWeAskedForVerification.begin();
while(itv1 != mWeAskedForVerification.end()){
if(itv1->second.nBlockHeight < pCurrentBlockIndex->nHeight - MAX_POSE_BLOCKS) {
mWeAskedForVerification.erase(itv1++);
} else {
++itv1;
}
}
// remove expired mapSeenMasternodeBroadcast
std::map<uint256, CMasternodeBroadcast>::iterator it3 = mapSeenMasternodeBroadcast.begin();
while(it3 != mapSeenMasternodeBroadcast.end()){
if((*it3).second.lastPing.sigTime < GetTime() - MASTERNODE_REMOVAL_SECONDS*2){
LogPrint("masternode", "CMasternodeMan::CheckAndRemove -- Removing expired Masternode broadcast: hash=%s\n", (*it3).second.GetHash().ToString());
mapSeenMasternodeBroadcast.erase(it3++);
} else {
++it3;
}
}
// remove expired mapSeenMasternodePing
std::map<uint256, CMasternodePing>::iterator it4 = mapSeenMasternodePing.begin();
while(it4 != mapSeenMasternodePing.end()){
if((*it4).second.sigTime < GetTime() - MASTERNODE_REMOVAL_SECONDS*2){
LogPrint("masternode", "CMasternodeMan::CheckAndRemove -- Removing expired Masternode ping: hash=%s\n", (*it4).second.GetHash().ToString());
mapSeenMasternodePing.erase(it4++);
} else {
++it4;
}
}
// remove expired mapSeenMasternodeVerification
std::map<uint256, CMasternodeVerification>::iterator itv2 = mapSeenMasternodeVerification.begin();
while(itv2 != mapSeenMasternodeVerification.end()){
if((*itv2).second.nBlockHeight < pCurrentBlockIndex->nHeight - MAX_POSE_BLOCKS){
LogPrint("masternode", "CMasternodeMan::CheckAndRemove -- Removing expired Masternode verification: hash=%s\n", (*itv2).first.ToString());
mapSeenMasternodeVerification.erase(itv2++);
} else {
++itv2;
}
}
LogPrintf("CMasternodeMan::CheckAndRemove -- %s\n", ToString());
if(fMasternodesRemoved) {
CheckAndRebuildMasternodeIndex();
} }
} }
// check who's asked for the Masternode list if(fMasternodesRemoved) {
std::map<CNetAddr, int64_t>::iterator it1 = mAskedUsForMasternodeList.begin(); NotifyMasternodeUpdates();
while(it1 != mAskedUsForMasternodeList.end()){
if((*it1).second < GetTime()) {
mAskedUsForMasternodeList.erase(it1++);
} else {
++it1;
}
} }
// check who we asked for the Masternode list
it1 = mWeAskedForMasternodeList.begin();
while(it1 != mWeAskedForMasternodeList.end()){
if((*it1).second < GetTime()){
mWeAskedForMasternodeList.erase(it1++);
} else {
++it1;
}
}
// check which Masternodes we've asked for
std::map<COutPoint, int64_t>::iterator it2 = mWeAskedForMasternodeListEntry.begin();
while(it2 != mWeAskedForMasternodeListEntry.end()){
if((*it2).second < GetTime()){
mWeAskedForMasternodeListEntry.erase(it2++);
} else {
++it2;
}
}
std::map<CNetAddr, CMasternodeVerification>::iterator itv1 = mWeAskedForVerification.begin();
while(itv1 != mWeAskedForVerification.end()){
if(itv1->second.nBlockHeight < pCurrentBlockIndex->nHeight - MAX_POSE_BLOCKS) {
mWeAskedForVerification.erase(itv1++);
} else {
++itv1;
}
}
// remove expired mapSeenMasternodeBroadcast
std::map<uint256, CMasternodeBroadcast>::iterator it3 = mapSeenMasternodeBroadcast.begin();
while(it3 != mapSeenMasternodeBroadcast.end()){
if((*it3).second.lastPing.sigTime < GetTime() - MASTERNODE_REMOVAL_SECONDS*2){
LogPrint("masternode", "CMasternodeMan::CheckAndRemove -- Removing expired Masternode broadcast: hash=%s\n", (*it3).second.GetHash().ToString());
mapSeenMasternodeBroadcast.erase(it3++);
} else {
++it3;
}
}
// remove expired mapSeenMasternodePing
std::map<uint256, CMasternodePing>::iterator it4 = mapSeenMasternodePing.begin();
while(it4 != mapSeenMasternodePing.end()){
if((*it4).second.sigTime < GetTime() - MASTERNODE_REMOVAL_SECONDS*2){
LogPrint("masternode", "CMasternodeMan::CheckAndRemove -- Removing expired Masternode ping: hash=%s\n", (*it4).second.GetHash().ToString());
mapSeenMasternodePing.erase(it4++);
} else {
++it4;
}
}
// remove expired mapSeenMasternodeVerification
std::map<uint256, CMasternodeVerification>::iterator itv2 = mapSeenMasternodeVerification.begin();
while(itv2 != mapSeenMasternodeVerification.end()){
if((*itv2).second.nBlockHeight < pCurrentBlockIndex->nHeight - MAX_POSE_BLOCKS){
LogPrint("masternode", "CMasternodeMan::CheckAndRemove -- Removing expired Masternode verification: hash=%s\n", (*itv2).first.ToString());
mapSeenMasternodeVerification.erase(itv2++);
} else {
++itv2;
}
}
LogPrintf("CMasternodeMan::CheckAndRemove -- %s\n", ToString());
} }
void CMasternodeMan::Clear() void CMasternodeMan::Clear()
@ -610,20 +695,25 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData
if (strCommand == NetMsgType::MNANNOUNCE) { //Masternode Broadcast if (strCommand == NetMsgType::MNANNOUNCE) { //Masternode Broadcast
CMasternodeBroadcast mnb; {
vRecv >> mnb; LOCK(cs);
int nDos = 0; CMasternodeBroadcast mnb;
vRecv >> mnb;
if (CheckMnbAndUpdateMasternodeList(mnb, nDos)) { int nDos = 0;
// use announced Masternode as a peer
addrman.Add(CAddress(mnb.addr), pfrom->addr, 2*60*60); if (CheckMnbAndUpdateMasternodeList(mnb, nDos)) {
} else if(nDos > 0) { // use announced Masternode as a peer
Misbehaving(pfrom->GetId(), nDos); addrman.Add(CAddress(mnb.addr), pfrom->addr, 2*60*60);
} else if(nDos > 0) {
Misbehaving(pfrom->GetId(), nDos);
}
}
if(fMasternodesAdded) {
NotifyMasternodeUpdates();
} }
} else if (strCommand == NetMsgType::MNPING) { //Masternode Ping } else if (strCommand == NetMsgType::MNPING) { //Masternode Ping
// ignore masternode pings until masternode list is synced // ignore masternode pings until masternode list is synced
if (!masternodeSync.IsMasternodeListSynced()) return; if (!masternodeSync.IsMasternodeListSynced()) return;
@ -657,7 +747,6 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData
AskForMN(pfrom, mnp.vin); AskForMN(pfrom, mnp.vin);
} else if (strCommand == NetMsgType::DSEG) { //Get Masternode list or specific entry } else if (strCommand == NetMsgType::DSEG) { //Get Masternode list or specific entry
// Ignore such requests until we are fully synced. // Ignore such requests until we are fully synced.
// We could start processing this after masternode list is synced // We could start processing this after masternode list is synced
// but this is a heavy one so it's better to finish sync first. // but this is a heavy one so it's better to finish sync first.
@ -722,6 +811,8 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData
} else if (strCommand == NetMsgType::MNVERIFY) { // Masternode Verify } else if (strCommand == NetMsgType::MNVERIFY) { // Masternode Verify
LOCK(cs);
CMasternodeVerification mnv; CMasternodeVerification mnv;
vRecv >> mnv; vRecv >> mnv;
@ -1290,6 +1381,32 @@ void CMasternodeMan::UpdateLastPaid(const CBlockIndex *pindex)
IsFirstRun = !masternodeSync.IsWinnersListSynced(); IsFirstRun = !masternodeSync.IsWinnersListSynced();
} }
void CMasternodeMan::CheckAndRebuildMasternodeIndex()
{
LOCK(cs);
if(GetTime() - nLastIndexRebuildTime < MIN_INDEX_REBUILD_TIME) {
return;
}
if(indexMasternodes.GetSize() <= MAX_EXPECTED_INDEX_SIZE) {
return;
}
if(indexMasternodes.GetSize() <= int(vMasternodes.size())) {
return;
}
indexMasternodesOld = indexMasternodes;
indexMasternodes.Clear();
for(size_t i = 0; i < vMasternodes.size(); ++i) {
indexMasternodes.AddMasternodeVIN(vMasternodes[i].vin);
}
fIndexRebuilt = true;
nLastIndexRebuildTime = GetTime();
}
void CMasternodeMan::UpdateWatchdogVoteTime(const CTxIn& vin) void CMasternodeMan::UpdateWatchdogVoteTime(const CTxIn& vin)
{ {
LOCK(cs); LOCK(cs);
@ -1406,3 +1523,26 @@ void CMasternodeMan::UpdatedBlockTip(const CBlockIndex *pindex)
UpdateLastPaid(pindex); UpdateLastPaid(pindex);
} }
} }
void CMasternodeMan::NotifyMasternodeUpdates()
{
// Avoid double locking
bool fMasternodesAddedLocal = false;
bool fMasternodesRemovedLocal = false;
{
LOCK(cs);
fMasternodesAddedLocal = fMasternodesAdded;
fMasternodesRemovedLocal = fMasternodesRemoved;
}
if(fMasternodesAddedLocal) {
governance.CheckMasternodeOrphanVotes();
}
if(fMasternodesRemovedLocal) {
governance.UpdateCachesAndClean();
}
LOCK(cs);
fMasternodesAdded = false;
fMasternodesRemoved = false;
}

View File

@ -14,9 +14,85 @@ class CMasternodeMan;
extern CMasternodeMan mnodeman; extern CMasternodeMan mnodeman;
/**
* Provides a forward and reverse index between MN vin's and integers.
*
* This mapping is normally add-only and is expected to be permanent
* It is only rebuilt if the size of the index exceeds the expected maximum number
* of MN's and the current number of known MN's.
*
* The external interface to this index is provided via delegation by CMasternodeMan
*/
class CMasternodeIndex
{
public: // Types
typedef std::map<CTxIn,int> index_m_t;
typedef index_m_t::iterator index_m_it;
typedef index_m_t::const_iterator index_m_cit;
typedef std::map<int,CTxIn> rindex_m_t;
typedef rindex_m_t::iterator rindex_m_it;
typedef rindex_m_t::const_iterator rindex_m_cit;
private:
int nSize;
index_m_t mapIndex;
rindex_m_t mapReverseIndex;
public:
CMasternodeIndex();
int GetSize() const {
return nSize;
}
/// Retrieve masternode vin by index
bool Get(int nIndex, CTxIn& vinMasternode) const;
/// Get index of a masternode vin
int GetMasternodeIndex(const CTxIn& vinMasternode) const;
void AddMasternodeVIN(const CTxIn& vinMasternode);
void Clear();
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
{
READWRITE(mapIndex);
if(ser_action.ForRead()) {
RebuildIndex();
}
}
private:
void RebuildIndex();
};
class CMasternodeMan class CMasternodeMan
{ {
public:
typedef std::map<CTxIn,int> index_m_t;
typedef index_m_t::iterator index_m_it;
typedef index_m_t::const_iterator index_m_cit;
private: private:
static const int MAX_EXPECTED_INDEX_SIZE = 30000;
/// Only allow 1 index rebuild per hour
static const int64_t MIN_INDEX_REBUILD_TIME = 3600;
static const std::string SERIALIZATION_VERSION_STRING; static const std::string SERIALIZATION_VERSION_STRING;
static const int DSEG_UPDATE_SECONDS = 3 * 60 * 60; static const int DSEG_UPDATE_SECONDS = 3 * 60 * 60;
@ -45,6 +121,21 @@ private:
// who we asked for the masternode verification // who we asked for the masternode verification
std::map<CNetAddr, CMasternodeVerification> mWeAskedForVerification; std::map<CNetAddr, CMasternodeVerification> mWeAskedForVerification;
int64_t nLastIndexRebuildTime;
CMasternodeIndex indexMasternodes;
CMasternodeIndex indexMasternodesOld;
/// Set when index has been rebuilt, clear when read
bool fIndexRebuilt;
/// Set when masternodes are added, cleared when CGovernanceManager is notified
bool fMasternodesAdded;
/// Set when masternodes are removed, cleared when CGovernanceManager is notified
bool fMasternodesRemoved;
std::vector<uint256> vecDirtyGovernanceObjectHashes; std::vector<uint256> vecDirtyGovernanceObjectHashes;
int64_t nLastWatchdogVoteTime; int64_t nLastWatchdogVoteTime;
@ -62,8 +153,6 @@ public:
int64_t nDsqCount; int64_t nDsqCount;
CMasternodeMan() : nLastWatchdogVoteTime(0), nDsqCount(0) {}
ADD_SERIALIZE_METHODS; ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation> template <typename Stream, typename Operation>
@ -87,16 +176,19 @@ public:
READWRITE(mapSeenMasternodeBroadcast); READWRITE(mapSeenMasternodeBroadcast);
READWRITE(mapSeenMasternodePing); READWRITE(mapSeenMasternodePing);
READWRITE(indexMasternodes);
if(ser_action.ForRead() && (strVersion != SERIALIZATION_VERSION_STRING)) { if(ser_action.ForRead() && (strVersion != SERIALIZATION_VERSION_STRING)) {
Clear(); Clear();
} }
} }
CMasternodeMan();
/// Add an entry /// Add an entry
bool Add(CMasternode &mn); bool Add(CMasternode &mn);
/// Ask (source) node for mnb /// Ask (source) node for mnb
void AskForMN(CNode *pnode, CTxIn &vin); void AskForMN(CNode *pnode, const CTxIn &vin);
/// Check all Masternodes /// Check all Masternodes
void Check(); void Check();
@ -128,6 +220,49 @@ public:
bool Get(const CPubKey& pubKeyMasternode, CMasternode& masternode); bool Get(const CPubKey& pubKeyMasternode, CMasternode& masternode);
bool Get(const CTxIn& vin, CMasternode& masternode); bool Get(const CTxIn& vin, CMasternode& masternode);
/// Retrieve masternode vin by index
bool Get(int nIndex, CTxIn& vinMasternode, bool& fIndexRebuiltOut) {
LOCK(cs);
fIndexRebuiltOut = fIndexRebuilt;
return indexMasternodes.Get(nIndex, vinMasternode);
}
bool GetIndexRebuiltFlag() {
LOCK(cs);
return fIndexRebuilt;
}
/// Get index of a masternode vin
int GetMasternodeIndex(const CTxIn& vinMasternode) {
LOCK(cs);
return indexMasternodes.GetMasternodeIndex(vinMasternode);
}
/// Get old index of a masternode vin
int GetMasternodeIndexOld(const CTxIn& vinMasternode) {
LOCK(cs);
return indexMasternodesOld.GetMasternodeIndex(vinMasternode);
}
/// Get masternode VIN for an old index value
bool GetMasternodeVinForIndexOld(int nMasternodeIndex, CTxIn& vinMasternodeOut) {
LOCK(cs);
return indexMasternodesOld.Get(nMasternodeIndex, vinMasternodeOut);
}
/// Get index of a masternode vin, returning rebuild flag
int GetMasternodeIndex(const CTxIn& vinMasternode, bool& fIndexRebuiltOut) {
LOCK(cs);
fIndexRebuiltOut = fIndexRebuilt;
return indexMasternodes.GetMasternodeIndex(vinMasternode);
}
void ClearOldMasternodeIndex() {
LOCK(cs);
indexMasternodesOld.Clear();
fIndexRebuilt = false;
}
bool Has(const CTxIn& vin); bool Has(const CTxIn& vin);
masternode_info_t GetMasternodeInfo(const CTxIn& vin); masternode_info_t GetMasternodeInfo(const CTxIn& vin);
@ -173,6 +308,8 @@ public:
void UpdateLastPaid(const CBlockIndex *pindex); void UpdateLastPaid(const CBlockIndex *pindex);
void CheckAndRebuildMasternodeIndex();
void AddDirtyGovernanceObjectHash(const uint256& nHash) void AddDirtyGovernanceObjectHash(const uint256& nHash)
{ {
LOCK(cs); LOCK(cs);
@ -202,6 +339,13 @@ public:
void SetMasternodeLastPing(const CTxIn& vin, const CMasternodePing& mnp); void SetMasternodeLastPing(const CTxIn& vin, const CMasternodePing& mnp);
void UpdatedBlockTip(const CBlockIndex *pindex); void UpdatedBlockTip(const CBlockIndex *pindex);
/**
* Called to notify CGovernanceManager that the masternode index has been updated.
* Must be called while not holding the CMasternodeMan::cs mutex
*/
void NotifyMasternodeUpdates();
}; };
#endif #endif

View File

@ -106,6 +106,7 @@ void MasternodeList::StartAlias(std::string strAlias)
strStatusHtml += "<br>Successfully started masternode."; strStatusHtml += "<br>Successfully started masternode.";
mnodeman.UpdateMasternodeList(mnb); mnodeman.UpdateMasternodeList(mnb);
mnb.Relay(); mnb.Relay();
mnodeman.NotifyMasternodeUpdates();
} else { } else {
strStatusHtml += "<br>Failed to start masternode.<br>Error: " + strError; strStatusHtml += "<br>Failed to start masternode.<br>Error: " + strError;
} }
@ -142,6 +143,7 @@ void MasternodeList::StartAll(std::string strCommand)
nCountSuccessful++; nCountSuccessful++;
mnodeman.UpdateMasternodeList(mnb); mnodeman.UpdateMasternodeList(mnb);
mnb.Relay(); mnb.Relay();
mnodeman.NotifyMasternodeUpdates();
} else { } else {
nCountFailed++; nCountFailed++;
strFailedHtml += "\nFailed to start " + mne.getAlias() + ". Error: " + strError; strFailedHtml += "\nFailed to start " + mne.getAlias() + ". Error: " + strError;

View File

@ -283,16 +283,15 @@ UniValue gobject(const UniValue& params, bool fHelp)
return returnObj; return returnObj;
} }
std::string strError = ""; CGovernanceException exception;
if(governance.AddOrUpdateVote(vote, NULL, strError)) { if(governance.ProcessVote(vote, exception)) {
governance.AddSeenVote(vote.GetHash(), SEEN_OBJECT_IS_VALID);
vote.Relay();
success++; success++;
statusObj.push_back(Pair("result", "success")); statusObj.push_back(Pair("result", "success"));
} else { }
else {
failed++; failed++;
statusObj.push_back(Pair("result", "failed")); statusObj.push_back(Pair("result", "failed"));
statusObj.push_back(Pair("errorMessage", strError.c_str())); statusObj.push_back(Pair("errorMessage", exception.GetMessage()));
} }
resultsObj.push_back(Pair("dash.conf", statusObj)); resultsObj.push_back(Pair("dash.conf", statusObj));
@ -386,15 +385,15 @@ UniValue gobject(const UniValue& params, bool fHelp)
continue; continue;
} }
if(governance.AddOrUpdateVote(vote, NULL, strError)) { CGovernanceException exception;
governance.AddSeenVote(vote.GetHash(), SEEN_OBJECT_IS_VALID); if(governance.ProcessVote(vote, exception)) {
vote.Relay();
success++; success++;
statusObj.push_back(Pair("result", "success")); statusObj.push_back(Pair("result", "success"));
} else { }
else {
failed++; failed++;
statusObj.push_back(Pair("result", "failed")); statusObj.push_back(Pair("result", "failed"));
statusObj.push_back(Pair("errorMessage", strError.c_str())); statusObj.push_back(Pair("errorMessage", exception.GetMessage()));
} }
resultsObj.push_back(Pair(mne.getAlias(), statusObj)); resultsObj.push_back(Pair(mne.getAlias(), statusObj));
@ -511,15 +510,15 @@ UniValue gobject(const UniValue& params, bool fHelp)
// UPDATE LOCAL DATABASE WITH NEW OBJECT SETTINGS // UPDATE LOCAL DATABASE WITH NEW OBJECT SETTINGS
if(governance.AddOrUpdateVote(vote, NULL, strError)) { CGovernanceException exception;
governance.AddSeenVote(vote.GetHash(), SEEN_OBJECT_IS_VALID); if(governance.ProcessVote(vote, exception)) {
vote.Relay();
success++; success++;
statusObj.push_back(Pair("result", "success")); statusObj.push_back(Pair("result", "success"));
} else { }
else {
failed++; failed++;
statusObj.push_back(Pair("result", "failed")); statusObj.push_back(Pair("result", "failed"));
statusObj.push_back(Pair("errorMessage", strError.c_str())); statusObj.push_back(Pair("errorMessage", exception.GetMessage()));
} }
resultsObj.push_back(Pair(mne.getAlias(), statusObj)); resultsObj.push_back(Pair(mne.getAlias(), statusObj));
@ -573,13 +572,13 @@ UniValue gobject(const UniValue& params, bool fHelp)
BOOST_FOREACH(CGovernanceObject* pGovObj, objs) BOOST_FOREACH(CGovernanceObject* pGovObj, objs)
{ {
// IF WE HAVE A SPECIFIC NODE REQUESTED TO VOTE, DO THAT // IF WE HAVE A SPECIFIC NODE REQUESTED TO VOTE, DO THAT
if(strShow == "valid" && !pGovObj->fCachedValid) continue; if(strShow == "valid" && !pGovObj->IsSetCachedValid()) continue;
UniValue bObj(UniValue::VOBJ); UniValue bObj(UniValue::VOBJ);
bObj.push_back(Pair("DataHex", pGovObj->GetDataAsHex())); bObj.push_back(Pair("DataHex", pGovObj->GetDataAsHex()));
bObj.push_back(Pair("DataString", pGovObj->GetDataAsString())); bObj.push_back(Pair("DataString", pGovObj->GetDataAsString()));
bObj.push_back(Pair("Hash", pGovObj->GetHash().ToString())); bObj.push_back(Pair("Hash", pGovObj->GetHash().ToString()));
bObj.push_back(Pair("CollateralHash", pGovObj->nCollateralHash.ToString())); bObj.push_back(Pair("CollateralHash", pGovObj->GetCollateralHash().ToString()));
// REPORT STATUS FOR FUNDING VOTES SPECIFICALLY // REPORT STATUS FOR FUNDING VOTES SPECIFICALLY
bObj.push_back(Pair("AbsoluteYesCount", pGovObj->GetAbsoluteYesCount(VOTE_SIGNAL_FUNDING))); bObj.push_back(Pair("AbsoluteYesCount", pGovObj->GetAbsoluteYesCount(VOTE_SIGNAL_FUNDING)));
@ -591,10 +590,10 @@ UniValue gobject(const UniValue& params, bool fHelp)
std::string strError = ""; std::string strError = "";
bObj.push_back(Pair("fBlockchainValidity", pGovObj->IsValidLocally(pindex , strError, false))); bObj.push_back(Pair("fBlockchainValidity", pGovObj->IsValidLocally(pindex , strError, false)));
bObj.push_back(Pair("IsValidReason", strError.c_str())); bObj.push_back(Pair("IsValidReason", strError.c_str()));
bObj.push_back(Pair("fCachedValid", pGovObj->fCachedValid)); bObj.push_back(Pair("fCachedValid", pGovObj->IsSetCachedValid()));
bObj.push_back(Pair("fCachedFunding", pGovObj->fCachedFunding)); bObj.push_back(Pair("fCachedFunding", pGovObj->IsSetCachedFunding()));
bObj.push_back(Pair("fCachedDelete", pGovObj->fCachedDelete)); bObj.push_back(Pair("fCachedDelete", pGovObj->IsSetCachedDelete()));
bObj.push_back(Pair("fCachedEndorsed", pGovObj->fCachedEndorsed)); bObj.push_back(Pair("fCachedEndorsed", pGovObj->IsSetCachedEndorsed()));
objResult.push_back(Pair(pGovObj->GetHash().ToString(), bObj)); objResult.push_back(Pair(pGovObj->GetHash().ToString(), bObj));
} }
@ -625,7 +624,7 @@ UniValue gobject(const UniValue& params, bool fHelp)
objResult.push_back(Pair("DataHex", pGovObj->GetDataAsHex())); objResult.push_back(Pair("DataHex", pGovObj->GetDataAsHex()));
objResult.push_back(Pair("DataString", pGovObj->GetDataAsString())); objResult.push_back(Pair("DataString", pGovObj->GetDataAsString()));
objResult.push_back(Pair("Hash", pGovObj->GetHash().ToString())); objResult.push_back(Pair("Hash", pGovObj->GetHash().ToString()));
objResult.push_back(Pair("CollateralHash", pGovObj->nCollateralHash.ToString())); objResult.push_back(Pair("CollateralHash", pGovObj->GetCollateralHash().ToString()));
// SHOW (MUCH MORE) INFORMATION ABOUT VOTES FOR GOVERNANCE OBJECT (THAN LIST/DIFF ABOVE) // SHOW (MUCH MORE) INFORMATION ABOUT VOTES FOR GOVERNANCE OBJECT (THAN LIST/DIFF ABOVE)
// -- FUNDING VOTING RESULTS // -- FUNDING VOTING RESULTS
@ -665,11 +664,10 @@ UniValue gobject(const UniValue& params, bool fHelp)
std::string strError = ""; std::string strError = "";
objResult.push_back(Pair("fLocalValidity", pGovObj->IsValidLocally(chainActive.Tip(), strError, false))); objResult.push_back(Pair("fLocalValidity", pGovObj->IsValidLocally(chainActive.Tip(), strError, false)));
objResult.push_back(Pair("IsValidReason", strError.c_str())); objResult.push_back(Pair("IsValidReason", strError.c_str()));
objResult.push_back(Pair("fCachedValid", pGovObj->fCachedValid)); objResult.push_back(Pair("fCachedValid", pGovObj->IsSetCachedValid()));
objResult.push_back(Pair("fCachedFunding", pGovObj->fCachedFunding)); objResult.push_back(Pair("fCachedFunding", pGovObj->IsSetCachedFunding()));
objResult.push_back(Pair("fCachedDelete", pGovObj->fCachedDelete)); objResult.push_back(Pair("fCachedDelete", pGovObj->IsSetCachedDelete()));
objResult.push_back(Pair("fCachedEndorsed", pGovObj->fCachedEndorsed)); objResult.push_back(Pair("fCachedEndorsed", pGovObj->IsSetCachedEndorsed()));
return objResult; return objResult;
} }
@ -701,9 +699,9 @@ UniValue gobject(const UniValue& params, bool fHelp)
// GET MATCHING VOTES BY HASH, THEN SHOW USERS VOTE INFORMATION // GET MATCHING VOTES BY HASH, THEN SHOW USERS VOTE INFORMATION
std::vector<CGovernanceVote*> vecVotes = governance.GetMatchingVotes(hash); std::vector<CGovernanceVote> vecVotes = governance.GetMatchingVotes(hash);
BOOST_FOREACH(CGovernanceVote* pVote, vecVotes) { BOOST_FOREACH(CGovernanceVote vote, vecVotes) {
bResult.push_back(Pair(pVote->GetHash().ToString(), pVote->ToString())); bResult.push_back(Pair(vote.GetHash().ToString(), vote.ToString()));
} }
return bResult; return bResult;
@ -764,13 +762,12 @@ UniValue voteraw(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_INTERNAL_ERROR, "Failure to verify vote."); throw JSONRPCError(RPC_INTERNAL_ERROR, "Failure to verify vote.");
} }
std::string strError = ""; CGovernanceException exception;
if(governance.AddOrUpdateVote(vote, NULL, strError)) { if(governance.ProcessVote(vote, exception)) {
governance.AddSeenVote(vote.GetHash(), SEEN_OBJECT_IS_VALID);
vote.Relay();
return "Voted successfully"; return "Voted successfully";
} else { }
throw JSONRPCError(RPC_INTERNAL_ERROR, "Error voting : " + strError); else {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Error voting : " + exception.GetMessage());
} }
} }

View File

@ -272,6 +272,7 @@ UniValue masternode(const UniValue& params, bool fHelp)
} else { } else {
statusObj.push_back(Pair("errorMessage", strError)); statusObj.push_back(Pair("errorMessage", strError));
} }
mnodeman.NotifyMasternodeUpdates();
break; break;
} }
} }
@ -328,6 +329,7 @@ UniValue masternode(const UniValue& params, bool fHelp)
resultsObj.push_back(Pair("status", statusObj)); resultsObj.push_back(Pair("status", statusObj));
} }
mnodeman.NotifyMasternodeUpdates();
UniValue returnObj(UniValue::VOBJ); UniValue returnObj(UniValue::VOBJ);
returnObj.push_back(Pair("overall", strprintf("Successfully started %d masternodes, failed to start %d, total %d", nSuccessful, nFailed, nSuccessful + nFailed))); returnObj.push_back(Pair("overall", strprintf("Successfully started %d masternodes, failed to start %d, total %d", nSuccessful, nFailed, nSuccessful + nFailed)));
@ -781,6 +783,7 @@ UniValue masternodebroadcast(const UniValue& params, bool fHelp)
mnb.Relay(); mnb.Relay();
fResult = true; fResult = true;
} }
mnodeman.NotifyMasternodeUpdates();
} else fResult = false; } else fResult = false;
if(fResult) { if(fResult) {

View File

@ -12,6 +12,7 @@
#include <assert.h> #include <assert.h>
#include <ios> #include <ios>
#include <limits> #include <limits>
#include <list>
#include <map> #include <map>
#include <set> #include <set>
#include <stdint.h> #include <stdint.h>
@ -886,6 +887,39 @@ void Unserialize(Stream& is, std::set<K, Pred, A>& m, int nType, int nVersion)
} }
} }
/**
* list
*/
template<typename T, typename A>
unsigned int GetSerializeSize(const std::list<T, A>& l, int nType, int nVersion)
{
unsigned int nSize = GetSizeOfCompactSize(l.size());
for (typename std::list<T, A>::const_iterator it = l.begin(); it != l.end(); ++it)
nSize += GetSerializeSize((*it), nType, nVersion);
return nSize;
}
template<typename Stream, typename T, typename A>
void Serialize(Stream& os, const std::list<T, A>& l, int nType, int nVersion)
{
WriteCompactSize(os, l.size());
for (typename std::list<T, A>::const_iterator it = l.begin(); it != l.end(); ++it)
Serialize(os, (*it), nType, nVersion);
}
template<typename Stream, typename T, typename A>
void Unserialize(Stream& is, std::list<T, A>& l, int nType, int nVersion)
{
l.clear();
unsigned int nSize = ReadCompactSize(is);
for (unsigned int i = 0; i < nSize; i++)
{
T val;
Unserialize(is, val, nType, nVersion);
l.push_back(val);
}
}
/** /**

127
src/test/cachemap_tests.cpp Normal file
View File

@ -0,0 +1,127 @@
// Copyright (c) 2014-2016 The Dash Core developers
#include "cachemap.h"
#include "test/test_dash.h"
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(cachemap_tests, BasicTestingSetup)
bool Compare(const CacheMap<int,int>& map1, const CacheMap<int,int>& map2 )
{
if(map1.GetMaxSize() != map2.GetMaxSize()) {
return false;
}
if(map1.GetSize() != map2.GetSize()) {
return false;
}
const CacheMap<int,int>::list_t& items1 = map1.GetItemList();
for(CacheMap<int,int>::list_cit it = items1.begin(); it != items1.end(); ++it) {
if(!map2.HasKey(it->key)) {
return false;
}
int val = 0;
if(!map2.Get(it->key, val)) {
return false;
}
if(it->value != val) {
return false;
}
}
const CacheMap<int,int>::list_t& items2 = map2.GetItemList();
for(CacheMap<int,int>::list_cit it = items2.begin(); it != items2.end(); ++it) {
if(!map1.HasKey(it->key)) {
return false;
}
int val = 0;
if(!map1.Get(it->key, val)) {
return false;
}
if(it->value != val) {
return false;
}
}
return true;
}
BOOST_AUTO_TEST_CASE(cachemap_test)
{
// create a CacheMap limited to 10 items
CacheMap<int,int> mapTest1(10);
// check that the max size is 10
BOOST_CHECK(mapTest1.GetMaxSize() == 10);
// check that the size is 0
BOOST_CHECK(mapTest1.GetSize() == 0);
// insert (-1, -1)
mapTest1.Insert(-1, -1);
// make sure that the size is updated
BOOST_CHECK(mapTest1.GetSize() == 1);
// make sure the map contains the key
BOOST_CHECK(mapTest1.HasKey(-1) == true);
// add 10 items
for(int i = 0; i < 10; ++i) {
mapTest1.Insert(i, i);
}
// check that the size is 10
BOOST_CHECK(mapTest1.GetSize() == 10);
// check that the map contains the expected items
for(int i = 0; i < 10; ++i) {
int nVal = 0;
BOOST_CHECK(mapTest1.Get(i, nVal) == true);
BOOST_CHECK(nVal == i);
}
// check that the map no longer contains the first item
BOOST_CHECK(mapTest1.HasKey(-1) == false);
// erase an item
mapTest1.Erase(5);
// check the size
BOOST_CHECK(mapTest1.GetSize() == 9);
// check that the map no longer contains the item
BOOST_CHECK(mapTest1.HasKey(5) == false);
// check that the map contains the expected items
int expected[] = { 0, 1, 2, 3, 4, 6, 7, 8, 9 };
for(size_t i = 0; i < 9; ++i) {
int nVal = 0;
int eVal = expected[i];
BOOST_CHECK(mapTest1.Get(eVal, nVal) == true);
BOOST_CHECK(nVal == eVal);
}
// test serialization
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << mapTest1;
CacheMap<int,int> mapTest2;
ss >> mapTest2;
BOOST_CHECK(Compare(mapTest1, mapTest2));
// test copy constructor
CacheMap<int,int> mapTest3(mapTest1);
BOOST_CHECK(Compare(mapTest1, mapTest3));
// test assignment operator
CacheMap<int,int> mapTest4;
mapTest4 = mapTest1;
BOOST_CHECK(Compare(mapTest1, mapTest4));
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -0,0 +1,176 @@
// Copyright (c) 2014-2016 The Dash Core developers
#include "cachemultimap.h"
#include "test/test_dash.h"
#include <algorithm>
#include <iostream>
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(cachemultimap_tests, BasicTestingSetup)
void DumpMap(const CacheMultiMap<int,int>& map)
{
const CacheMultiMap<int,int>::list_t& listItems = map.GetItemList();
for(CacheMultiMap<int,int>::list_cit it = listItems.begin(); it != listItems.end(); ++it) {
const CacheItem<int,int>& item = *it;
std::cout << item.key << " : " << item.value << std::endl;
}
}
bool Compare(const CacheMultiMap<int,int>& map1, const CacheMultiMap<int,int>& map2 )
{
if(map1.GetMaxSize() != map2.GetMaxSize()) {
std::cout << "Compare returning false: max size mismatch" << std::endl;
return false;
}
if(map1.GetSize() != map2.GetSize()) {
std::cout << "Compare returning false: size mismatch" << std::endl;
return false;
}
const CacheMultiMap<int,int>::list_t& items1 = map1.GetItemList();
const CacheMultiMap<int,int>::list_t& items2 = map2.GetItemList();
CacheMultiMap<int,int>::list_cit it2 = items2.begin();
for(CacheMultiMap<int,int>::list_cit it1 = items1.begin(); it1 != items1.end(); ++it1) {
const CacheItem<int,int>& item1 = *it1;
const CacheItem<int,int>& item2 = *it2;
if(item1.key != item2.key) {
return false;
}
if(item1.value != item2.value) {
return false;
}
++it2;
}
return true;
}
bool CheckExpected(const CacheMultiMap<int,int>& map, int* expected, CacheMultiMap<int,int>::size_type nSize)
{
if(map.GetSize() != nSize) {
return false;
}
for(CacheMultiMap<int,int>::size_type i = 0; i < nSize; ++i) {
int nVal = 0;
int eVal = expected[i];
if(!map.Get(eVal, nVal)) {
return false;
}
if(nVal != eVal) {
return false;
}
}
return true;
}
BOOST_AUTO_TEST_CASE(cachemultimap_test)
{
// create a CacheMultiMap limited to 10 items
CacheMultiMap<int,int> mapTest1(10);
// check that the max size is 10
BOOST_CHECK(mapTest1.GetMaxSize() == 10);
// check that the size is 0
BOOST_CHECK(mapTest1.GetSize() == 0);
// insert (-1, -1)
mapTest1.Insert(-1, -1);
// make sure that the size is updated
BOOST_CHECK(mapTest1.GetSize() == 1);
// make sure the map contains the key
BOOST_CHECK(mapTest1.HasKey(-1) == true);
// add 10 items
for(int i = 0; i < 10; ++i) {
mapTest1.Insert(i, i);
}
// check that the size is 10
BOOST_CHECK(mapTest1.GetSize() == 10);
// check that the map contains the expected items
for(int i = 0; i < 10; ++i) {
int nVal = 0;
BOOST_CHECK(mapTest1.Get(i, nVal) == true);
BOOST_CHECK(nVal == i);
}
// check that the map no longer contains the first item
BOOST_CHECK(mapTest1.HasKey(-1) == false);
// erase an item
mapTest1.Erase(5);
// check the size
BOOST_CHECK(mapTest1.GetSize() == 9);
// check that the map no longer contains the item
BOOST_CHECK(mapTest1.HasKey(5) == false);
// check that the map contains the expected items
int expected[] = { 0, 1, 2, 3, 4, 6, 7, 8, 9 };
BOOST_CHECK(CheckExpected(mapTest1, expected, 9 ) == true);
// add multiple items for the same key
mapTest1.Insert(5, 2);
mapTest1.Insert(5, 1);
mapTest1.Insert(5, 4);
// check the size
BOOST_CHECK(mapTest1.GetSize() == 10);
// check that 2 keys have been removed
BOOST_CHECK(mapTest1.HasKey(0) == false);
BOOST_CHECK(mapTest1.HasKey(1) == false);
BOOST_CHECK(mapTest1.HasKey(2) == true);
// check multiple values
std::vector<int> vecVals;
BOOST_CHECK(mapTest1.GetAll(5, vecVals) == true);
BOOST_CHECK(vecVals.size() == 3);
BOOST_CHECK(vecVals[0] == 1);
BOOST_CHECK(vecVals[1] == 2);
BOOST_CHECK(vecVals[2] == 4);
// std::cout << "mapTest1 dump:" << std::endl;
// DumpMap(mapTest1);
// test serialization
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << mapTest1;
CacheMultiMap<int,int> mapTest2;
ss >> mapTest2;
// std::cout << "mapTest2 dump:" << std::endl;
// DumpMap(mapTest2);
// check multiple values
std::vector<int> vecVals2;
BOOST_CHECK(mapTest2.GetAll(5, vecVals2) == true);
BOOST_CHECK(vecVals2.size() == 3);
BOOST_CHECK(vecVals2[0] == 1);
BOOST_CHECK(vecVals2[1] == 2);
BOOST_CHECK(vecVals2[2] == 4);
BOOST_CHECK(Compare(mapTest1, mapTest2));
// test copy constructor
CacheMultiMap<int,int> mapTest3(mapTest1);
BOOST_CHECK(Compare(mapTest1, mapTest3));
// test assignment operator
CacheMultiMap<int,int> mapTest4;
mapTest4 = mapTest1;
BOOST_CHECK(Compare(mapTest1, mapTest4));
}
BOOST_AUTO_TEST_SUITE_END()