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/pow_tests.cpp \
|
||||
test/prevector_tests.cpp \
|
||||
test/ratecheck_tests.cpp \
|
||||
test/reverselock_tests.cpp \
|
||||
test/rpc_tests.cpp \
|
||||
test/sanity_tests.cpp \
|
||||
|
@ -28,7 +28,7 @@ std::map<uint256, int64_t> mapAskedForGovernanceObject;
|
||||
|
||||
int nSubmittedFinalBudget;
|
||||
|
||||
const std::string CGovernanceManager::SERIALIZATION_VERSION_STRING = "CGovernanceManager-Version-4";
|
||||
const std::string CGovernanceManager::SERIALIZATION_VERSION_STRING = "CGovernanceManager-Version-5";
|
||||
|
||||
CGovernanceManager::CGovernanceManager()
|
||||
: pCurrentBlockIndex(NULL),
|
||||
@ -745,13 +745,13 @@ bool CGovernanceManager::MasternodeRateCheck(const CGovernanceObject& govobj, bo
|
||||
|
||||
if(it == mapLastMasternodeObject.end()) {
|
||||
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) {
|
||||
case GOVERNANCE_OBJECT_TRIGGER:
|
||||
it->second.nLastTriggerTime = std::max(it->second.nLastTriggerTime, nTimestamp);
|
||||
it->second.triggerBuffer.AddTimestamp(nTimestamp);
|
||||
break;
|
||||
case GOVERNANCE_OBJECT_WATCHDOG:
|
||||
it->second.nLastWatchdogTime = std::max(it->second.nLastWatchdogTime, nTimestamp);
|
||||
it->second.watchdogBuffer.AddTimestamp(nTimestamp);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -773,35 +773,40 @@ bool CGovernanceManager::MasternodeRateCheck(const CGovernanceObject& govobj, bo
|
||||
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",
|
||||
strHash, vin.prevout.ToStringShort(), nTimestamp, nNow);
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t nMinDiff = 0;
|
||||
int64_t nLastObjectTime = 0;
|
||||
|
||||
double dMaxRate = 1.1 / nSuperblockCycleSeconds;
|
||||
double dRate = 0.0;
|
||||
CRateCheckBuffer buffer;
|
||||
switch(nObjectType) {
|
||||
case GOVERNANCE_OBJECT_TRIGGER:
|
||||
// Allow 1 trigger per mn per cycle, with a small fudge factor
|
||||
nMinDiff = int64_t(0.9 * nSuperblockCycleSeconds);
|
||||
nLastObjectTime = it->second.nLastTriggerTime;
|
||||
dMaxRate = 1.1 / nSuperblockCycleSeconds;
|
||||
buffer = it->second.triggerBuffer;
|
||||
buffer.AddTimestamp(nTimestamp);
|
||||
dRate = buffer.GetRate();
|
||||
if(fUpdateLast) {
|
||||
it->second.nLastTriggerTime = std::max(it->second.nLastTriggerTime, nTimestamp);
|
||||
it->second.triggerBuffer.AddTimestamp(nTimestamp);
|
||||
}
|
||||
break;
|
||||
case GOVERNANCE_OBJECT_WATCHDOG:
|
||||
nMinDiff = Params().GetConsensus().nPowTargetSpacing;
|
||||
nLastObjectTime = it->second.nLastWatchdogTime;
|
||||
dMaxRate = 1.1 / 3600.;
|
||||
buffer = it->second.watchdogBuffer;
|
||||
buffer.AddTimestamp(nTimestamp);
|
||||
dRate = buffer.GetRate();
|
||||
if(fUpdateLast) {
|
||||
it->second.nLastWatchdogTime = std::max(it->second.nLastWatchdogTime, nTimestamp);
|
||||
it->second.watchdogBuffer.AddTimestamp(nTimestamp);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if((nTimestamp - nLastObjectTime) > nMinDiff) {
|
||||
if(dRate < dMaxRate) {
|
||||
if(fUpdateLast) {
|
||||
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",
|
||||
strHash, vin.prevout.ToStringShort(), nTimestamp, nLastObjectTime, nMinDiff);
|
||||
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, dRate, dMaxRate);
|
||||
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;
|
||||
|
||||
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
|
||||
//
|
||||
@ -49,9 +155,9 @@ class CGovernanceManager
|
||||
|
||||
public: // Types
|
||||
struct last_object_rec {
|
||||
last_object_rec(int64_t nLastTriggerTimeIn = 0, int64_t nLastWatchdogTimeIn = 0, bool fStatusOKIn = true)
|
||||
: nLastTriggerTime(nLastTriggerTimeIn),
|
||||
nLastWatchdogTime(nLastWatchdogTimeIn),
|
||||
last_object_rec(bool fStatusOKIn = true)
|
||||
: triggerBuffer(),
|
||||
watchdogBuffer(),
|
||||
fStatusOK(fStatusOKIn)
|
||||
{}
|
||||
|
||||
@ -60,13 +166,13 @@ public: // Types
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
|
||||
{
|
||||
READWRITE(nLastTriggerTime);
|
||||
READWRITE(nLastWatchdogTime);
|
||||
READWRITE(triggerBuffer);
|
||||
READWRITE(watchdogBuffer);
|
||||
READWRITE(fStatusOK);
|
||||
}
|
||||
|
||||
int64_t nLastTriggerTime;
|
||||
int64_t nLastWatchdogTime;
|
||||
CRateCheckBuffer triggerBuffer;
|
||||
CRateCheckBuffer watchdogBuffer;
|
||||
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