New vote tallying implementation (#1135)
This commit is contained in:
parent
82ca5fdbb8
commit
c31ba8ba4c
@ -74,6 +74,8 @@ BITCOIN_CORE_H = \
|
||||
arith_uint256.h \
|
||||
base58.h \
|
||||
bloom.h \
|
||||
cachemap.h \
|
||||
cachemultimap.h \
|
||||
chain.h \
|
||||
chainparams.h \
|
||||
chainparamsbase.h \
|
||||
@ -99,7 +101,9 @@ BITCOIN_CORE_H = \
|
||||
darksend-relay.h \
|
||||
governance.h \
|
||||
governance-classes.h \
|
||||
governance-exceptions.h \
|
||||
governance-vote.h \
|
||||
governance-votedb.h \
|
||||
flat-database.h \
|
||||
hash.h \
|
||||
httprpc.h \
|
||||
@ -202,6 +206,7 @@ libbitcoin_server_a_SOURCES = \
|
||||
governance.cpp \
|
||||
governance-classes.cpp \
|
||||
governance-vote.cpp \
|
||||
governance-votedb.cpp \
|
||||
main.cpp \
|
||||
merkleblock.cpp \
|
||||
miner.cpp \
|
||||
|
@ -43,6 +43,8 @@ BITCOIN_TESTS =\
|
||||
test/base64_tests.cpp \
|
||||
test/bip32_tests.cpp \
|
||||
test/bloom_tests.cpp \
|
||||
test/cachemap_tests.cpp \
|
||||
test/cachemultimap_tests.cpp \
|
||||
test/checkblock_tests.cpp \
|
||||
test/Checkpoints_tests.cpp \
|
||||
test/coins_tests.cpp \
|
||||
|
@ -271,6 +271,7 @@ void CActiveMasternode::ManageStateLocal()
|
||||
//update to masternode list
|
||||
LogPrintf("CActiveMasternode::ManageStateLocal -- Update Masternode List\n");
|
||||
mnodeman.UpdateMasternodeList(mnb);
|
||||
mnodeman.NotifyMasternodeUpdates();
|
||||
|
||||
//send to all peers
|
||||
LogPrintf("CActiveMasternode::ManageStateLocal -- Relay broadcast, vin=%s\n", vin.ToString());
|
||||
|
202
src/cachemap.h
Normal file
202
src/cachemap.h
Normal 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
254
src/cachemultimap.h
Normal 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_ */
|
@ -346,7 +346,7 @@ bool CSuperblockManager::IsSuperblockTriggered(int nBlockHeight)
|
||||
|
||||
// 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");
|
||||
DBG( cout << "IsSuperblockTriggered returning true" << endl; );
|
||||
return true;
|
||||
@ -506,7 +506,7 @@ CSuperblock(uint256& nHash)
|
||||
|
||||
DBG( cout << "CSuperblock Constructor pGovObj : "
|
||||
<< pGovObj->GetDataAsString()
|
||||
<< ", nObjectType = " << pGovObj->nObjectType
|
||||
<< ", nObjectType = " << pGovObj->GetObjectType()
|
||||
<< endl; );
|
||||
|
||||
if (pGovObj->GetObjectType() != GOVERNANCE_OBJECT_TRIGGER) {
|
||||
@ -678,7 +678,7 @@ bool CSuperblock::IsValid(const CTransaction& txNew, int nBlockHeight, CAmount b
|
||||
int nMinerPayments = nOutputs - nPayments;
|
||||
|
||||
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
|
||||
// superblock payments and the payments actually in the block, after
|
||||
|
@ -1,8 +1,8 @@
|
||||
// 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 GOVERANCE_CLASSES_H
|
||||
#define GOVERANCE_CLASSES_H
|
||||
#ifndef GOVERNANCE_CLASSES_H
|
||||
#define GOVERNANCE_CLASSES_H
|
||||
|
||||
//#define ENABLE_DASH_DEBUG
|
||||
|
||||
|
98
src/governance-exceptions.h
Normal file
98
src/governance-exceptions.h
Normal 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
|
@ -2,8 +2,8 @@
|
||||
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
#ifndef GOVERANCE_MISC_H
|
||||
#define GOVERANCE_MISC_H
|
||||
#ifndef GOVERNANCE_MISC_H
|
||||
#define GOVERNANCE_MISC_H
|
||||
|
||||
#include "main.h"
|
||||
#include "governance.h"
|
||||
|
@ -233,7 +233,7 @@ CGovernanceVote::CGovernanceVote(CTxIn vinMasternodeIn, uint256 nParentHashIn, v
|
||||
vchSig()
|
||||
{}
|
||||
|
||||
void CGovernanceVote::Relay()
|
||||
void CGovernanceVote::Relay() const
|
||||
{
|
||||
CInv inv(MSG_GOVERNANCE_OBJECT_VOTE, GetHash());
|
||||
RelayInv(inv, PROTOCOL_VERSION);
|
||||
@ -262,7 +262,7 @@ bool CGovernanceVote::Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CGovernanceVote::IsValid(bool fSignatureCheck)
|
||||
bool CGovernanceVote::IsValid(bool fSignatureCheck) const
|
||||
{
|
||||
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));
|
||||
@ -270,7 +270,7 @@ bool CGovernanceVote::IsValid(bool fSignatureCheck)
|
||||
}
|
||||
|
||||
// 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());
|
||||
return false;
|
||||
@ -302,3 +302,44 @@ bool CGovernanceVote::IsValid(bool fSignatureCheck)
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
#ifndef GOVERANCE_VOTE_H
|
||||
#define GOVERANCE_VOTE_H
|
||||
#ifndef GOVERNANCE_VOTE_H
|
||||
#define GOVERNANCE_VOTE_H
|
||||
|
||||
#include "main.h"
|
||||
#include "sync.h"
|
||||
@ -68,6 +68,8 @@ enum vote_signal_enum_t {
|
||||
VOTE_SIGNAL_CUSTOM20 = 35
|
||||
};
|
||||
|
||||
static const int MAX_SUPPORTED_VOTE_SIGNAL = VOTE_SIGNAL_ENDORSED;
|
||||
|
||||
/**
|
||||
* Governance Voting
|
||||
*
|
||||
@ -89,6 +91,10 @@ public:
|
||||
|
||||
class CGovernanceVote
|
||||
{
|
||||
friend bool operator==(const CGovernanceVote& vote1, const CGovernanceVote& vote2);
|
||||
|
||||
friend bool operator<(const CGovernanceVote& vote1, const CGovernanceVote& vote2);
|
||||
|
||||
private:
|
||||
bool fValid; //if the vote is currently valid / counted
|
||||
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; }
|
||||
|
||||
bool Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode);
|
||||
bool IsValid(bool fSignatureCheck);
|
||||
void Relay();
|
||||
bool IsValid(bool fSignatureCheck) const;
|
||||
void Relay() const;
|
||||
|
||||
std::string GetVoteString() const {
|
||||
return CGovernanceVoting::ConvertOutcomeToString(GetOutcome());
|
||||
}
|
||||
|
||||
CTxIn& GetVinMasternode() { return vinMasternode; }
|
||||
|
||||
@ -144,7 +154,7 @@ public:
|
||||
return ss.GetHash();
|
||||
}
|
||||
|
||||
std::string ToString()
|
||||
std::string ToString() const
|
||||
{
|
||||
std::ostringstream ostr;
|
||||
ostr << vinMasternode.ToString() << ":"
|
||||
@ -197,6 +207,7 @@ public:
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 12.1.1 - CGovernanceVoteManager
|
||||
* -------------------------------
|
||||
|
84
src/governance-votedb.cpp
Normal file
84
src/governance-votedb.cpp
Normal 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
93
src/governance-votedb.h
Normal 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
346
src/governance.h
346
src/governance.h
@ -2,8 +2,8 @@
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef GOVERANCE_H
|
||||
#define GOVERANCE_H
|
||||
#ifndef GOVERNANCE_H
|
||||
#define GOVERNANCE_H
|
||||
|
||||
//#define ENABLE_DASH_DEBUG
|
||||
|
||||
@ -15,17 +15,22 @@
|
||||
#include "util.h"
|
||||
#include "base58.h"
|
||||
#include "masternode.h"
|
||||
#include "governance-exceptions.h"
|
||||
#include "governance-vote.h"
|
||||
#include "governance-votedb.h"
|
||||
#include "masternodeman.h"
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "init.h"
|
||||
#include <univalue.h>
|
||||
#include "utilstrencodings.h"
|
||||
#include "cachemap.h"
|
||||
#include "cachemultimap.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
class CGovernanceManager;
|
||||
class CGovernanceTriggerManager;
|
||||
class CGovernanceObject;
|
||||
class CGovernanceVote;
|
||||
|
||||
@ -57,6 +62,8 @@ extern CGovernanceManager governance;
|
||||
//
|
||||
class CGovernanceManager
|
||||
{
|
||||
friend class CGovernanceObject;
|
||||
|
||||
public: // Types
|
||||
|
||||
typedef std::map<uint256, CGovernanceObject> object_m_t;
|
||||
@ -65,6 +72,8 @@ public: // Types
|
||||
|
||||
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 count_m_t::iterator count_m_it;
|
||||
@ -77,11 +86,9 @@ public: // Types
|
||||
|
||||
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 transaction_m_t::const_iterator transaction_m_cit;
|
||||
typedef CacheMultiMap<uint256, CGovernanceVote> vote_mcache_t;
|
||||
|
||||
typedef object_m_t::size_type size_type;
|
||||
|
||||
@ -91,10 +98,17 @@ public: // Types
|
||||
|
||||
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
|
||||
const CBlockIndex *pCurrentBlockIndex;
|
||||
|
||||
@ -105,57 +119,68 @@ private:
|
||||
object_m_t mapObjects;
|
||||
|
||||
count_m_t mapSeenGovernanceObjects;
|
||||
count_m_t mapSeenVotes;
|
||||
vote_m_t mapOrphanVotes;
|
||||
|
||||
// todo: one of these should point to the other
|
||||
// -- must be carefully managed while adding/removing/updating
|
||||
vote_m_t mapVotesByHash;
|
||||
vote_m_t mapVotesByType;
|
||||
object_ref_cache_t mapVoteToObject;
|
||||
|
||||
vote_cache_t mapInvalidVotes;
|
||||
|
||||
vote_mcache_t mapOrphanVotes;
|
||||
|
||||
txout_m_t mapLastMasternodeTrigger;
|
||||
|
||||
hash_s_t setRequestedObjects;
|
||||
|
||||
hash_s_t setRequestedVotes;
|
||||
|
||||
public:
|
||||
// critical section to protect the inner data structures
|
||||
mutable CCriticalSection cs;
|
||||
|
||||
CGovernanceManager();
|
||||
|
||||
virtual ~CGovernanceManager() {}
|
||||
|
||||
void ClearSeen()
|
||||
{
|
||||
LOCK(cs);
|
||||
mapSeenGovernanceObjects.clear();
|
||||
mapSeenVotes.clear();
|
||||
}
|
||||
|
||||
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 SyncParentObjectByVote(CNode* pfrom, const CGovernanceVote& vote);
|
||||
|
||||
void ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv);
|
||||
|
||||
void NewBlock();
|
||||
|
||||
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);
|
||||
|
||||
int CountMatchingVotes(CGovernanceObject& govobj, vote_signal_enum_t nVoteSignalIn, vote_outcome_enum_t nVoteOutcomeIn);
|
||||
|
||||
bool IsBudgetPaymentBlock(int nBlockHeight);
|
||||
bool AddGovernanceObject (CGovernanceObject& govobj);
|
||||
bool AddOrUpdateVote(const CGovernanceVote& vote, CNode* pfrom, std::string& strError);
|
||||
|
||||
std::string GetRequiredPaymentsString(int nBlockHeight);
|
||||
void CleanAndRemove(bool fSignatureCheck);
|
||||
void UpdateCachesAndClean();
|
||||
void CheckAndRemove() {UpdateCachesAndClean();}
|
||||
|
||||
void CheckOrphanVotes();
|
||||
void UpdateCachesAndClean();
|
||||
|
||||
void CheckAndRemove() {UpdateCachesAndClean();}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
@ -164,10 +189,9 @@ public:
|
||||
LogPrint("gobject", "Governance object manager was cleared\n");
|
||||
mapObjects.clear();
|
||||
mapSeenGovernanceObjects.clear();
|
||||
mapSeenVotes.clear();
|
||||
mapOrphanVotes.clear();
|
||||
mapVotesByType.clear();
|
||||
mapVotesByHash.clear();
|
||||
mapVoteToObject.Clear();
|
||||
mapInvalidVotes.Clear();
|
||||
mapOrphanVotes.Clear();
|
||||
mapLastMasternodeTrigger.clear();
|
||||
}
|
||||
|
||||
@ -178,14 +202,25 @@ public:
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
LOCK(cs);
|
||||
std::string strVersion;
|
||||
if(ser_action.ForRead()) {
|
||||
READWRITE(strVersion);
|
||||
}
|
||||
else {
|
||||
strVersion = SERIALIZATION_VERSION_STRING;
|
||||
READWRITE(strVersion);
|
||||
}
|
||||
READWRITE(mapSeenGovernanceObjects);
|
||||
READWRITE(mapSeenVotes);
|
||||
READWRITE(mapInvalidVotes);
|
||||
READWRITE(mapOrphanVotes);
|
||||
READWRITE(mapObjects);
|
||||
READWRITE(mapVotesByHash);
|
||||
READWRITE(mapVotesByType);
|
||||
READWRITE(mapLastMasternodeTrigger);
|
||||
if(ser_action.ForRead() && (strVersion != SERIALIZATION_VERSION_STRING)) {
|
||||
Clear();
|
||||
return;
|
||||
}
|
||||
if(ser_action.ForRead()) {
|
||||
RebuildIndexes();
|
||||
AddCachedTriggers();
|
||||
}
|
||||
}
|
||||
@ -211,11 +246,90 @@ public:
|
||||
|
||||
bool MasternodeRateCheck(const CTxIn& vin, int nObjectType);
|
||||
|
||||
bool ProcessVote(const CGovernanceVote& vote, CGovernanceException& exception) {
|
||||
return ProcessVote(NULL, vote, exception);
|
||||
}
|
||||
|
||||
void CheckMasternodeOrphanVotes();
|
||||
|
||||
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();
|
||||
|
||||
};
|
||||
|
||||
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
|
||||
*
|
||||
@ -223,41 +337,140 @@ private:
|
||||
|
||||
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:
|
||||
// critical section to protect the inner data structures
|
||||
/// critical section to protect the inner data structures
|
||||
mutable CCriticalSection cs;
|
||||
|
||||
public:
|
||||
|
||||
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
|
||||
/// Object typecode
|
||||
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;
|
||||
std::vector<unsigned char> vchSig;
|
||||
|
||||
bool fCachedLocalValidity; // is valid by blockchain
|
||||
/// is valid by blockchain
|
||||
bool fCachedLocalValidity;
|
||||
std::string strLocalValidityError;
|
||||
|
||||
// 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)
|
||||
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 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 support has been reached for this object to be funded (doesn't mean it will for sure though)
|
||||
bool fCachedFunding;
|
||||
|
||||
/// 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(uint256 nHashParentIn, int nRevisionIn, int64_t nTime, uint256 nCollateralHashIn, std::string strDataIn);
|
||||
|
||||
CGovernanceObject(const CGovernanceObject& other);
|
||||
|
||||
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
|
||||
|
||||
void SetMasternodeInfo(const CTxIn& vin);
|
||||
@ -272,8 +485,9 @@ public:
|
||||
bool IsCollateralValid(std::string& strError);
|
||||
|
||||
void UpdateLocalValidity(const CBlockIndex *pCurrentBlockIndex);
|
||||
|
||||
void UpdateSentinelVariables(const CBlockIndex *pCurrentBlockIndex);
|
||||
int GetObjectType();
|
||||
|
||||
int GetObjectSubtype();
|
||||
|
||||
CAmount GetMinCollateralFee();
|
||||
@ -281,15 +495,18 @@ public:
|
||||
UniValue GetJSONObject();
|
||||
|
||||
void Relay();
|
||||
|
||||
uint256 GetHash();
|
||||
|
||||
// GET VOTE COUNT FOR SIGNAL
|
||||
|
||||
int GetAbsoluteYesCount(vote_signal_enum_t eVoteSignalIn);
|
||||
int GetAbsoluteNoCount(vote_signal_enum_t eVoteSignalIn);
|
||||
int GetYesCount(vote_signal_enum_t eVoteSignalIn);
|
||||
int GetNoCount(vote_signal_enum_t eVoteSignalIn);
|
||||
int GetAbstainCount(vote_signal_enum_t eVoteSignalIn);
|
||||
int CountMatchingVotes(vote_signal_enum_t eVoteSignalIn, vote_outcome_enum_t eVoteOutcomeIn) const;
|
||||
|
||||
int GetAbsoluteYesCount(vote_signal_enum_t eVoteSignalIn) const;
|
||||
int GetAbsoluteNoCount(vote_signal_enum_t eVoteSignalIn) const;
|
||||
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
|
||||
|
||||
@ -313,16 +530,33 @@ public:
|
||||
READWRITE(nObjectType);
|
||||
READWRITE(vinMasternode);
|
||||
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
|
||||
}
|
||||
|
||||
private:
|
||||
// FUNCTIONS FOR DEALING WITH DATA STRING
|
||||
|
||||
void LoadData();
|
||||
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();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
12
src/main.cpp
12
src/main.cpp
@ -4948,14 +4948,11 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||
return mapDarksendBroadcastTxes.count(inv.hash);
|
||||
|
||||
case MSG_GOVERNANCE_OBJECT:
|
||||
return governance.HaveObjectForHash(inv.hash);
|
||||
|
||||
case MSG_GOVERNANCE_OBJECT_VOTE:
|
||||
return governance.HaveVoteForHash(inv.hash);
|
||||
return ! governance.ConfirmInventoryRequest(inv);
|
||||
|
||||
case MSG_MASTERNODE_VERIFY:
|
||||
return mnodeman.mapSeenMasternodeVerification.count(inv.hash);
|
||||
|
||||
}
|
||||
|
||||
// Don't know what it is, just say we already got one
|
||||
@ -6720,9 +6717,15 @@ bool SendMessages(CNode* pto)
|
||||
//
|
||||
// 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)
|
||||
{
|
||||
const CInv& inv = (*pto->mapAskFor.begin()).second;
|
||||
LogPrint("net", "SendMessages (mapAskFor) -- inv = %s peer=%d\n", inv.ToString(), pto->id);
|
||||
if (!AlreadyHave(inv))
|
||||
{
|
||||
if (fDebug)
|
||||
@ -6735,6 +6738,7 @@ bool SendMessages(CNode* pto)
|
||||
}
|
||||
} else {
|
||||
//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->mapAskFor.erase(pto->mapAskFor.begin());
|
||||
|
@ -882,6 +882,14 @@ void CMasternode::UpdateWatchdogVoteTime()
|
||||
*/
|
||||
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::map<uint256, int>::iterator it = mapGovernanceObjectsVotedOn.begin();
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "masternodeman.h"
|
||||
#include "activemasternode.h"
|
||||
#include "darksend.h"
|
||||
#include "governance.h"
|
||||
#include "masternode.h"
|
||||
#include "masternode-payments.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
|
||||
|
||||
{
|
||||
bool operator()(const CMasternode* t1,
|
||||
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)
|
||||
{
|
||||
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);
|
||||
mn.nTimeLastWatchdogVote = mn.sigTime;
|
||||
vMasternodes.push_back(mn);
|
||||
indexMasternodes.AddMasternodeVIN(mn.vin);
|
||||
fMasternodesAdded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CMasternodeMan::AskForMN(CNode* pnode, CTxIn &vin)
|
||||
void CMasternodeMan::AskForMN(CNode* pnode, const CTxIn &vin)
|
||||
{
|
||||
if(!pnode) return;
|
||||
|
||||
@ -99,6 +173,7 @@ void CMasternodeMan::CheckAndRemove(bool fForceExpiredRemoval)
|
||||
|
||||
Check();
|
||||
|
||||
{
|
||||
LOCK(cs);
|
||||
|
||||
// Remove inactive and outdated masternodes
|
||||
@ -121,6 +196,7 @@ void CMasternodeMan::CheckAndRemove(bool fForceExpiredRemoval)
|
||||
|
||||
// and finally remove it from the list
|
||||
it = vMasternodes.erase(it);
|
||||
fMasternodesRemoved = true;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
@ -199,6 +275,15 @@ void CMasternodeMan::CheckAndRemove(bool fForceExpiredRemoval)
|
||||
}
|
||||
|
||||
LogPrintf("CMasternodeMan::CheckAndRemove -- %s\n", ToString());
|
||||
|
||||
if(fMasternodesRemoved) {
|
||||
CheckAndRebuildMasternodeIndex();
|
||||
}
|
||||
}
|
||||
|
||||
if(fMasternodesRemoved) {
|
||||
NotifyMasternodeUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
void CMasternodeMan::Clear()
|
||||
@ -610,6 +695,9 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData
|
||||
|
||||
if (strCommand == NetMsgType::MNANNOUNCE) { //Masternode Broadcast
|
||||
|
||||
{
|
||||
LOCK(cs);
|
||||
|
||||
CMasternodeBroadcast mnb;
|
||||
vRecv >> mnb;
|
||||
|
||||
@ -621,9 +709,11 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData
|
||||
} else if(nDos > 0) {
|
||||
Misbehaving(pfrom->GetId(), nDos);
|
||||
}
|
||||
|
||||
}
|
||||
if(fMasternodesAdded) {
|
||||
NotifyMasternodeUpdates();
|
||||
}
|
||||
} else if (strCommand == NetMsgType::MNPING) { //Masternode Ping
|
||||
|
||||
// ignore masternode pings until masternode list is synced
|
||||
if (!masternodeSync.IsMasternodeListSynced()) return;
|
||||
|
||||
@ -657,7 +747,6 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData
|
||||
AskForMN(pfrom, mnp.vin);
|
||||
|
||||
} else if (strCommand == NetMsgType::DSEG) { //Get Masternode list or specific entry
|
||||
|
||||
// Ignore such requests until we are fully 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.
|
||||
@ -722,6 +811,8 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData
|
||||
|
||||
} else if (strCommand == NetMsgType::MNVERIFY) { // Masternode Verify
|
||||
|
||||
LOCK(cs);
|
||||
|
||||
CMasternodeVerification mnv;
|
||||
vRecv >> mnv;
|
||||
|
||||
@ -1290,6 +1381,32 @@ void CMasternodeMan::UpdateLastPaid(const CBlockIndex *pindex)
|
||||
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)
|
||||
{
|
||||
LOCK(cs);
|
||||
@ -1406,3 +1523,26 @@ void CMasternodeMan::UpdatedBlockTip(const CBlockIndex *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;
|
||||
}
|
||||
|
@ -14,9 +14,85 @@ class CMasternodeMan;
|
||||
|
||||
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
|
||||
{
|
||||
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:
|
||||
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 int DSEG_UPDATE_SECONDS = 3 * 60 * 60;
|
||||
@ -45,6 +121,21 @@ private:
|
||||
// who we asked for the masternode verification
|
||||
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;
|
||||
|
||||
int64_t nLastWatchdogVoteTime;
|
||||
@ -62,8 +153,6 @@ public:
|
||||
int64_t nDsqCount;
|
||||
|
||||
|
||||
CMasternodeMan() : nLastWatchdogVoteTime(0), nDsqCount(0) {}
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
@ -87,16 +176,19 @@ public:
|
||||
|
||||
READWRITE(mapSeenMasternodeBroadcast);
|
||||
READWRITE(mapSeenMasternodePing);
|
||||
READWRITE(indexMasternodes);
|
||||
if(ser_action.ForRead() && (strVersion != SERIALIZATION_VERSION_STRING)) {
|
||||
Clear();
|
||||
}
|
||||
}
|
||||
|
||||
CMasternodeMan();
|
||||
|
||||
/// Add an entry
|
||||
bool Add(CMasternode &mn);
|
||||
|
||||
/// Ask (source) node for mnb
|
||||
void AskForMN(CNode *pnode, CTxIn &vin);
|
||||
void AskForMN(CNode *pnode, const CTxIn &vin);
|
||||
|
||||
/// Check all Masternodes
|
||||
void Check();
|
||||
@ -128,6 +220,49 @@ public:
|
||||
bool Get(const CPubKey& pubKeyMasternode, 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);
|
||||
|
||||
masternode_info_t GetMasternodeInfo(const CTxIn& vin);
|
||||
@ -173,6 +308,8 @@ public:
|
||||
|
||||
void UpdateLastPaid(const CBlockIndex *pindex);
|
||||
|
||||
void CheckAndRebuildMasternodeIndex();
|
||||
|
||||
void AddDirtyGovernanceObjectHash(const uint256& nHash)
|
||||
{
|
||||
LOCK(cs);
|
||||
@ -202,6 +339,13 @@ public:
|
||||
void SetMasternodeLastPing(const CTxIn& vin, const CMasternodePing& mnp);
|
||||
|
||||
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
|
||||
|
@ -106,6 +106,7 @@ void MasternodeList::StartAlias(std::string strAlias)
|
||||
strStatusHtml += "<br>Successfully started masternode.";
|
||||
mnodeman.UpdateMasternodeList(mnb);
|
||||
mnb.Relay();
|
||||
mnodeman.NotifyMasternodeUpdates();
|
||||
} else {
|
||||
strStatusHtml += "<br>Failed to start masternode.<br>Error: " + strError;
|
||||
}
|
||||
@ -142,6 +143,7 @@ void MasternodeList::StartAll(std::string strCommand)
|
||||
nCountSuccessful++;
|
||||
mnodeman.UpdateMasternodeList(mnb);
|
||||
mnb.Relay();
|
||||
mnodeman.NotifyMasternodeUpdates();
|
||||
} else {
|
||||
nCountFailed++;
|
||||
strFailedHtml += "\nFailed to start " + mne.getAlias() + ". Error: " + strError;
|
||||
|
@ -283,16 +283,15 @@ UniValue gobject(const UniValue& params, bool fHelp)
|
||||
return returnObj;
|
||||
}
|
||||
|
||||
std::string strError = "";
|
||||
if(governance.AddOrUpdateVote(vote, NULL, strError)) {
|
||||
governance.AddSeenVote(vote.GetHash(), SEEN_OBJECT_IS_VALID);
|
||||
vote.Relay();
|
||||
CGovernanceException exception;
|
||||
if(governance.ProcessVote(vote, exception)) {
|
||||
success++;
|
||||
statusObj.push_back(Pair("result", "success"));
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
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));
|
||||
@ -386,15 +385,15 @@ UniValue gobject(const UniValue& params, bool fHelp)
|
||||
continue;
|
||||
}
|
||||
|
||||
if(governance.AddOrUpdateVote(vote, NULL, strError)) {
|
||||
governance.AddSeenVote(vote.GetHash(), SEEN_OBJECT_IS_VALID);
|
||||
vote.Relay();
|
||||
CGovernanceException exception;
|
||||
if(governance.ProcessVote(vote, exception)) {
|
||||
success++;
|
||||
statusObj.push_back(Pair("result", "success"));
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
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));
|
||||
@ -511,15 +510,15 @@ UniValue gobject(const UniValue& params, bool fHelp)
|
||||
|
||||
// UPDATE LOCAL DATABASE WITH NEW OBJECT SETTINGS
|
||||
|
||||
if(governance.AddOrUpdateVote(vote, NULL, strError)) {
|
||||
governance.AddSeenVote(vote.GetHash(), SEEN_OBJECT_IS_VALID);
|
||||
vote.Relay();
|
||||
CGovernanceException exception;
|
||||
if(governance.ProcessVote(vote, exception)) {
|
||||
success++;
|
||||
statusObj.push_back(Pair("result", "success"));
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
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));
|
||||
@ -573,13 +572,13 @@ UniValue gobject(const UniValue& params, bool fHelp)
|
||||
BOOST_FOREACH(CGovernanceObject* pGovObj, objs)
|
||||
{
|
||||
// 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);
|
||||
bObj.push_back(Pair("DataHex", pGovObj->GetDataAsHex()));
|
||||
bObj.push_back(Pair("DataString", pGovObj->GetDataAsString()));
|
||||
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
|
||||
bObj.push_back(Pair("AbsoluteYesCount", pGovObj->GetAbsoluteYesCount(VOTE_SIGNAL_FUNDING)));
|
||||
@ -591,10 +590,10 @@ UniValue gobject(const UniValue& params, bool fHelp)
|
||||
std::string strError = "";
|
||||
bObj.push_back(Pair("fBlockchainValidity", pGovObj->IsValidLocally(pindex , strError, false)));
|
||||
bObj.push_back(Pair("IsValidReason", strError.c_str()));
|
||||
bObj.push_back(Pair("fCachedValid", pGovObj->fCachedValid));
|
||||
bObj.push_back(Pair("fCachedFunding", pGovObj->fCachedFunding));
|
||||
bObj.push_back(Pair("fCachedDelete", pGovObj->fCachedDelete));
|
||||
bObj.push_back(Pair("fCachedEndorsed", pGovObj->fCachedEndorsed));
|
||||
bObj.push_back(Pair("fCachedValid", pGovObj->IsSetCachedValid()));
|
||||
bObj.push_back(Pair("fCachedFunding", pGovObj->IsSetCachedFunding()));
|
||||
bObj.push_back(Pair("fCachedDelete", pGovObj->IsSetCachedDelete()));
|
||||
bObj.push_back(Pair("fCachedEndorsed", pGovObj->IsSetCachedEndorsed()));
|
||||
|
||||
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("DataString", pGovObj->GetDataAsString()));
|
||||
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)
|
||||
// -- FUNDING VOTING RESULTS
|
||||
@ -665,11 +664,10 @@ UniValue gobject(const UniValue& params, bool fHelp)
|
||||
std::string strError = "";
|
||||
objResult.push_back(Pair("fLocalValidity", pGovObj->IsValidLocally(chainActive.Tip(), strError, false)));
|
||||
objResult.push_back(Pair("IsValidReason", strError.c_str()));
|
||||
objResult.push_back(Pair("fCachedValid", pGovObj->fCachedValid));
|
||||
objResult.push_back(Pair("fCachedFunding", pGovObj->fCachedFunding));
|
||||
objResult.push_back(Pair("fCachedDelete", pGovObj->fCachedDelete));
|
||||
objResult.push_back(Pair("fCachedEndorsed", pGovObj->fCachedEndorsed));
|
||||
|
||||
objResult.push_back(Pair("fCachedValid", pGovObj->IsSetCachedValid()));
|
||||
objResult.push_back(Pair("fCachedFunding", pGovObj->IsSetCachedFunding()));
|
||||
objResult.push_back(Pair("fCachedDelete", pGovObj->IsSetCachedDelete()));
|
||||
objResult.push_back(Pair("fCachedEndorsed", pGovObj->IsSetCachedEndorsed()));
|
||||
return objResult;
|
||||
}
|
||||
|
||||
@ -701,9 +699,9 @@ UniValue gobject(const UniValue& params, bool fHelp)
|
||||
|
||||
// GET MATCHING VOTES BY HASH, THEN SHOW USERS VOTE INFORMATION
|
||||
|
||||
std::vector<CGovernanceVote*> vecVotes = governance.GetMatchingVotes(hash);
|
||||
BOOST_FOREACH(CGovernanceVote* pVote, vecVotes) {
|
||||
bResult.push_back(Pair(pVote->GetHash().ToString(), pVote->ToString()));
|
||||
std::vector<CGovernanceVote> vecVotes = governance.GetMatchingVotes(hash);
|
||||
BOOST_FOREACH(CGovernanceVote vote, vecVotes) {
|
||||
bResult.push_back(Pair(vote.GetHash().ToString(), vote.ToString()));
|
||||
}
|
||||
|
||||
return bResult;
|
||||
@ -764,13 +762,12 @@ UniValue voteraw(const UniValue& params, bool fHelp)
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Failure to verify vote.");
|
||||
}
|
||||
|
||||
std::string strError = "";
|
||||
if(governance.AddOrUpdateVote(vote, NULL, strError)) {
|
||||
governance.AddSeenVote(vote.GetHash(), SEEN_OBJECT_IS_VALID);
|
||||
vote.Relay();
|
||||
CGovernanceException exception;
|
||||
if(governance.ProcessVote(vote, exception)) {
|
||||
return "Voted successfully";
|
||||
} else {
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Error voting : " + strError);
|
||||
}
|
||||
else {
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Error voting : " + exception.GetMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -272,6 +272,7 @@ UniValue masternode(const UniValue& params, bool fHelp)
|
||||
} else {
|
||||
statusObj.push_back(Pair("errorMessage", strError));
|
||||
}
|
||||
mnodeman.NotifyMasternodeUpdates();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -328,6 +329,7 @@ UniValue masternode(const UniValue& params, bool fHelp)
|
||||
|
||||
resultsObj.push_back(Pair("status", statusObj));
|
||||
}
|
||||
mnodeman.NotifyMasternodeUpdates();
|
||||
|
||||
UniValue returnObj(UniValue::VOBJ);
|
||||
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();
|
||||
fResult = true;
|
||||
}
|
||||
mnodeman.NotifyMasternodeUpdates();
|
||||
} else fResult = false;
|
||||
|
||||
if(fResult) {
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <assert.h>
|
||||
#include <ios>
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#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
127
src/test/cachemap_tests.cpp
Normal 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()
|
176
src/test/cachemultimap_tests.cpp
Normal file
176
src/test/cachemultimap_tests.cpp
Normal 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()
|
Loading…
Reference in New Issue
Block a user