Implement rate check using buffer (#1202)
* Implemented buffer for masternode rate checks * Change upper limit on object timestamp to 1 hour in the future
This commit is contained in:
parent
711a5fbf20
commit
8fc8e6c8c2
@ -66,6 +66,7 @@ BITCOIN_TESTS =\
|
|||||||
test/policyestimator_tests.cpp \
|
test/policyestimator_tests.cpp \
|
||||||
test/pow_tests.cpp \
|
test/pow_tests.cpp \
|
||||||
test/prevector_tests.cpp \
|
test/prevector_tests.cpp \
|
||||||
|
test/ratecheck_tests.cpp \
|
||||||
test/reverselock_tests.cpp \
|
test/reverselock_tests.cpp \
|
||||||
test/rpc_tests.cpp \
|
test/rpc_tests.cpp \
|
||||||
test/sanity_tests.cpp \
|
test/sanity_tests.cpp \
|
||||||
|
@ -28,7 +28,7 @@ std::map<uint256, int64_t> mapAskedForGovernanceObject;
|
|||||||
|
|
||||||
int nSubmittedFinalBudget;
|
int nSubmittedFinalBudget;
|
||||||
|
|
||||||
const std::string CGovernanceManager::SERIALIZATION_VERSION_STRING = "CGovernanceManager-Version-4";
|
const std::string CGovernanceManager::SERIALIZATION_VERSION_STRING = "CGovernanceManager-Version-5";
|
||||||
|
|
||||||
CGovernanceManager::CGovernanceManager()
|
CGovernanceManager::CGovernanceManager()
|
||||||
: pCurrentBlockIndex(NULL),
|
: pCurrentBlockIndex(NULL),
|
||||||
@ -745,13 +745,13 @@ bool CGovernanceManager::MasternodeRateCheck(const CGovernanceObject& govobj, bo
|
|||||||
|
|
||||||
if(it == mapLastMasternodeObject.end()) {
|
if(it == mapLastMasternodeObject.end()) {
|
||||||
if(fUpdateLast) {
|
if(fUpdateLast) {
|
||||||
it = mapLastMasternodeObject.insert(txout_m_t::value_type(vin.prevout, last_object_rec(0, 0, true))).first;
|
it = mapLastMasternodeObject.insert(txout_m_t::value_type(vin.prevout, last_object_rec(true))).first;
|
||||||
switch(nObjectType) {
|
switch(nObjectType) {
|
||||||
case GOVERNANCE_OBJECT_TRIGGER:
|
case GOVERNANCE_OBJECT_TRIGGER:
|
||||||
it->second.nLastTriggerTime = std::max(it->second.nLastTriggerTime, nTimestamp);
|
it->second.triggerBuffer.AddTimestamp(nTimestamp);
|
||||||
break;
|
break;
|
||||||
case GOVERNANCE_OBJECT_WATCHDOG:
|
case GOVERNANCE_OBJECT_WATCHDOG:
|
||||||
it->second.nLastWatchdogTime = std::max(it->second.nLastWatchdogTime, nTimestamp);
|
it->second.watchdogBuffer.AddTimestamp(nTimestamp);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -773,35 +773,40 @@ bool CGovernanceManager::MasternodeRateCheck(const CGovernanceObject& govobj, bo
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(nTimestamp > nNow + 2 * nSuperblockCycleSeconds) {
|
if(nTimestamp > nNow + 60*60) {
|
||||||
LogPrintf("CGovernanceManager::MasternodeRateCheck -- object %s rejected due to too new (future) timestamp, masternode vin = %s, timestamp = %d, current time = %d\n",
|
LogPrintf("CGovernanceManager::MasternodeRateCheck -- object %s rejected due to too new (future) timestamp, masternode vin = %s, timestamp = %d, current time = %d\n",
|
||||||
strHash, vin.prevout.ToStringShort(), nTimestamp, nNow);
|
strHash, vin.prevout.ToStringShort(), nTimestamp, nNow);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t nMinDiff = 0;
|
double dMaxRate = 1.1 / nSuperblockCycleSeconds;
|
||||||
int64_t nLastObjectTime = 0;
|
double dRate = 0.0;
|
||||||
|
CRateCheckBuffer buffer;
|
||||||
switch(nObjectType) {
|
switch(nObjectType) {
|
||||||
case GOVERNANCE_OBJECT_TRIGGER:
|
case GOVERNANCE_OBJECT_TRIGGER:
|
||||||
// Allow 1 trigger per mn per cycle, with a small fudge factor
|
// Allow 1 trigger per mn per cycle, with a small fudge factor
|
||||||
nMinDiff = int64_t(0.9 * nSuperblockCycleSeconds);
|
dMaxRate = 1.1 / nSuperblockCycleSeconds;
|
||||||
nLastObjectTime = it->second.nLastTriggerTime;
|
buffer = it->second.triggerBuffer;
|
||||||
|
buffer.AddTimestamp(nTimestamp);
|
||||||
|
dRate = buffer.GetRate();
|
||||||
if(fUpdateLast) {
|
if(fUpdateLast) {
|
||||||
it->second.nLastTriggerTime = std::max(it->second.nLastTriggerTime, nTimestamp);
|
it->second.triggerBuffer.AddTimestamp(nTimestamp);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GOVERNANCE_OBJECT_WATCHDOG:
|
case GOVERNANCE_OBJECT_WATCHDOG:
|
||||||
nMinDiff = Params().GetConsensus().nPowTargetSpacing;
|
dMaxRate = 1.1 / 3600.;
|
||||||
nLastObjectTime = it->second.nLastWatchdogTime;
|
buffer = it->second.watchdogBuffer;
|
||||||
|
buffer.AddTimestamp(nTimestamp);
|
||||||
|
dRate = buffer.GetRate();
|
||||||
if(fUpdateLast) {
|
if(fUpdateLast) {
|
||||||
it->second.nLastWatchdogTime = std::max(it->second.nLastWatchdogTime, nTimestamp);
|
it->second.watchdogBuffer.AddTimestamp(nTimestamp);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if((nTimestamp - nLastObjectTime) > nMinDiff) {
|
if(dRate < dMaxRate) {
|
||||||
if(fUpdateLast) {
|
if(fUpdateLast) {
|
||||||
it->second.fStatusOK = true;
|
it->second.fStatusOK = true;
|
||||||
}
|
}
|
||||||
@ -813,8 +818,8 @@ bool CGovernanceManager::MasternodeRateCheck(const CGovernanceObject& govobj, bo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LogPrintf("CGovernanceManager::MasternodeRateCheck -- Rate too high: object hash = %s, masternode vin = %s, object timestamp = %d, last timestamp = %d, minimum difference = %d\n",
|
LogPrintf("CGovernanceManager::MasternodeRateCheck -- Rate too high: object hash = %s, masternode vin = %s, object timestamp = %d, rate = %f, max rate = %f\n",
|
||||||
strHash, vin.prevout.ToStringShort(), nTimestamp, nLastObjectTime, nMinDiff);
|
strHash, vin.prevout.ToStringShort(), nTimestamp, dRate, dMaxRate);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
120
src/governance.h
120
src/governance.h
@ -40,6 +40,112 @@ extern CGovernanceManager governance;
|
|||||||
|
|
||||||
typedef std::pair<CGovernanceObject, int64_t> object_time_pair_t;
|
typedef std::pair<CGovernanceObject, int64_t> object_time_pair_t;
|
||||||
|
|
||||||
|
static const int RATE_BUFFER_SIZE = 5;
|
||||||
|
|
||||||
|
class CRateCheckBuffer {
|
||||||
|
private:
|
||||||
|
std::vector<int64_t> vecTimestamps;
|
||||||
|
|
||||||
|
int nDataStart;
|
||||||
|
|
||||||
|
int nDataEnd;
|
||||||
|
|
||||||
|
bool fBufferEmpty;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CRateCheckBuffer()
|
||||||
|
: vecTimestamps(RATE_BUFFER_SIZE),
|
||||||
|
nDataStart(0),
|
||||||
|
nDataEnd(0),
|
||||||
|
fBufferEmpty(true)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void AddTimestamp(int64_t nTimestamp)
|
||||||
|
{
|
||||||
|
if((nDataEnd == nDataStart) && !fBufferEmpty) {
|
||||||
|
// Buffer full, discard 1st element
|
||||||
|
nDataStart = (nDataStart + 1) % RATE_BUFFER_SIZE;
|
||||||
|
}
|
||||||
|
vecTimestamps[nDataEnd] = nTimestamp;
|
||||||
|
nDataEnd = (nDataEnd + 1) % RATE_BUFFER_SIZE;
|
||||||
|
fBufferEmpty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t GetMinTimestamp()
|
||||||
|
{
|
||||||
|
int nIndex = nDataStart;
|
||||||
|
int64_t nMin = numeric_limits<int64_t>::max();
|
||||||
|
if(fBufferEmpty) {
|
||||||
|
return nMin;
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
if(vecTimestamps[nIndex] < nMin) {
|
||||||
|
nMin = vecTimestamps[nIndex];
|
||||||
|
}
|
||||||
|
nIndex = (nIndex + 1) % RATE_BUFFER_SIZE;
|
||||||
|
} while(nIndex != nDataEnd);
|
||||||
|
return nMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t GetMaxTimestamp()
|
||||||
|
{
|
||||||
|
int nIndex = nDataStart;
|
||||||
|
int64_t nMax = 0;
|
||||||
|
if(fBufferEmpty) {
|
||||||
|
return nMax;
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
if(vecTimestamps[nIndex] > nMax) {
|
||||||
|
nMax = vecTimestamps[nIndex];
|
||||||
|
}
|
||||||
|
nIndex = (nIndex + 1) % RATE_BUFFER_SIZE;
|
||||||
|
} while(nIndex != nDataEnd);
|
||||||
|
return nMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetCount()
|
||||||
|
{
|
||||||
|
int nCount = 0;
|
||||||
|
if(fBufferEmpty) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(nDataEnd > nDataStart) {
|
||||||
|
nCount = nDataEnd - nDataStart;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
nCount = RATE_BUFFER_SIZE - nDataStart + nDataEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
double GetRate()
|
||||||
|
{
|
||||||
|
int nCount = GetCount();
|
||||||
|
if(nCount < 2) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
int64_t nMin = GetMinTimestamp();
|
||||||
|
int64_t nMax = GetMaxTimestamp();
|
||||||
|
if(nMin == nMax) {
|
||||||
|
// multiple objects with the same timestamp => infinite rate
|
||||||
|
return 1.0e10;
|
||||||
|
}
|
||||||
|
return double(nCount) / double(nMax - nMin);
|
||||||
|
}
|
||||||
|
|
||||||
|
ADD_SERIALIZE_METHODS;
|
||||||
|
|
||||||
|
template <typename Stream, typename Operation>
|
||||||
|
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
|
||||||
|
{
|
||||||
|
READWRITE(vecTimestamps);
|
||||||
|
READWRITE(nDataStart);
|
||||||
|
READWRITE(nDataEnd);
|
||||||
|
READWRITE(fBufferEmpty);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Governance Manager : Contains all proposals for the budget
|
// Governance Manager : Contains all proposals for the budget
|
||||||
//
|
//
|
||||||
@ -49,9 +155,9 @@ class CGovernanceManager
|
|||||||
|
|
||||||
public: // Types
|
public: // Types
|
||||||
struct last_object_rec {
|
struct last_object_rec {
|
||||||
last_object_rec(int64_t nLastTriggerTimeIn = 0, int64_t nLastWatchdogTimeIn = 0, bool fStatusOKIn = true)
|
last_object_rec(bool fStatusOKIn = true)
|
||||||
: nLastTriggerTime(nLastTriggerTimeIn),
|
: triggerBuffer(),
|
||||||
nLastWatchdogTime(nLastWatchdogTimeIn),
|
watchdogBuffer(),
|
||||||
fStatusOK(fStatusOKIn)
|
fStatusOK(fStatusOKIn)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@ -60,13 +166,13 @@ public: // Types
|
|||||||
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)
|
||||||
{
|
{
|
||||||
READWRITE(nLastTriggerTime);
|
READWRITE(triggerBuffer);
|
||||||
READWRITE(nLastWatchdogTime);
|
READWRITE(watchdogBuffer);
|
||||||
READWRITE(fStatusOK);
|
READWRITE(fStatusOK);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t nLastTriggerTime;
|
CRateCheckBuffer triggerBuffer;
|
||||||
int64_t nLastWatchdogTime;
|
CRateCheckBuffer watchdogBuffer;
|
||||||
bool fStatusOK;
|
bool fStatusOK;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
81
src/test/ratecheck_tests.cpp
Normal file
81
src/test/ratecheck_tests.cpp
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// Copyright (c) 2014-2016 The Dash Core developers
|
||||||
|
|
||||||
|
#include "governance.h"
|
||||||
|
|
||||||
|
#include "test/test_dash.h"
|
||||||
|
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_SUITE(ratecheck_tests, BasicTestingSetup)
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(ratecheck_test)
|
||||||
|
{
|
||||||
|
CRateCheckBuffer buffer;
|
||||||
|
|
||||||
|
BOOST_CHECK(buffer.GetCount() == 0);
|
||||||
|
BOOST_CHECK(buffer.GetMinTimestamp() == numeric_limits<int64_t>::max());
|
||||||
|
BOOST_CHECK(buffer.GetMaxTimestamp() == 0);
|
||||||
|
BOOST_CHECK(buffer.GetRate() == 0.0);
|
||||||
|
|
||||||
|
buffer.AddTimestamp(1);
|
||||||
|
|
||||||
|
std::cout << "buffer.GetMinTimestamp() = " << buffer.GetMinTimestamp() << std::endl;
|
||||||
|
|
||||||
|
BOOST_CHECK(buffer.GetCount() == 1);
|
||||||
|
BOOST_CHECK(buffer.GetMinTimestamp() == 1);
|
||||||
|
BOOST_CHECK(buffer.GetMaxTimestamp() == 1);
|
||||||
|
BOOST_CHECK(buffer.GetRate() == 0.0);
|
||||||
|
|
||||||
|
buffer.AddTimestamp(2);
|
||||||
|
BOOST_CHECK(buffer.GetCount() == 2);
|
||||||
|
BOOST_CHECK(buffer.GetMinTimestamp() == 1);
|
||||||
|
BOOST_CHECK(buffer.GetMaxTimestamp() == 2);
|
||||||
|
BOOST_CHECK(fabs(buffer.GetRate() - 2.0) < 1.0e-9);
|
||||||
|
|
||||||
|
buffer.AddTimestamp(3);
|
||||||
|
BOOST_CHECK(buffer.GetCount() == 3);
|
||||||
|
BOOST_CHECK(buffer.GetMinTimestamp() == 1);
|
||||||
|
BOOST_CHECK(buffer.GetMaxTimestamp() == 3);
|
||||||
|
|
||||||
|
int64_t nMin = buffer.GetMinTimestamp();
|
||||||
|
int64_t nMax = buffer.GetMaxTimestamp();
|
||||||
|
double dRate = buffer.GetRate();
|
||||||
|
|
||||||
|
std::cout << "buffer.GetCount() = " << buffer.GetCount() << std::endl;
|
||||||
|
std::cout << "nMin = " << nMin << std::endl;
|
||||||
|
std::cout << "nMax = " << nMax << std::endl;
|
||||||
|
std::cout << "buffer.GetRate() = " << dRate << std::endl;
|
||||||
|
|
||||||
|
BOOST_CHECK(fabs(buffer.GetRate() - (3.0/2.0)) < 1.0e-9);
|
||||||
|
|
||||||
|
buffer.AddTimestamp(4);
|
||||||
|
BOOST_CHECK(buffer.GetCount() == 4);
|
||||||
|
BOOST_CHECK(buffer.GetMinTimestamp() == 1);
|
||||||
|
BOOST_CHECK(buffer.GetMaxTimestamp() == 4);
|
||||||
|
BOOST_CHECK(fabs(buffer.GetRate() - (4.0/3.0)) < 1.0e-9);
|
||||||
|
|
||||||
|
buffer.AddTimestamp(5);
|
||||||
|
BOOST_CHECK(buffer.GetCount() == 5);
|
||||||
|
BOOST_CHECK(buffer.GetMinTimestamp() == 1);
|
||||||
|
BOOST_CHECK(buffer.GetMaxTimestamp() == 5);
|
||||||
|
BOOST_CHECK(fabs(buffer.GetRate() - (5.0/4.0)) < 1.0e-9);
|
||||||
|
|
||||||
|
buffer.AddTimestamp(6);
|
||||||
|
BOOST_CHECK(buffer.GetCount() == 5);
|
||||||
|
BOOST_CHECK(buffer.GetMinTimestamp() == 2);
|
||||||
|
BOOST_CHECK(buffer.GetMaxTimestamp() == 6);
|
||||||
|
BOOST_CHECK(fabs(buffer.GetRate() - (5.0/4.0)) < 1.0e-9);
|
||||||
|
|
||||||
|
CRateCheckBuffer buffer2;
|
||||||
|
|
||||||
|
std::cout << "Before loop tests" << std::endl;
|
||||||
|
for(int64_t i = 1; i < 11; ++i) {
|
||||||
|
std::cout << "In loop: i = " << i << std::endl;
|
||||||
|
buffer2.AddTimestamp(i);
|
||||||
|
BOOST_CHECK(buffer2.GetCount() == (i <= 5 ? i : 5));
|
||||||
|
BOOST_CHECK(buffer2.GetMinTimestamp() == max(int64_t(1), i - 4));
|
||||||
|
BOOST_CHECK(buffer2.GetMaxTimestamp() == i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
Loading…
Reference in New Issue
Block a user