Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ BITCOIN_TESTS =\
test/sigopcount_tests.cpp \
test/skiplist_tests.cpp \
test/sock_tests.cpp \
test/spork_tests.cpp \
test/streams_tests.cpp \
test/subsidy_tests.cpp \
test/sync_tests.cpp \
Expand Down
2 changes: 1 addition & 1 deletion src/bitcoin-chainstate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ int main(int argc, char* argv[])
// SETUP: Chainstate
const ChainstateManager::Options chainman_opts{
.chainparams = chainparams,
.adjusted_time_callback = static_cast<int64_t (*)()>(GetTime),
.adjusted_time_callback = NodeClock::now,
};
ChainstateManager chainman{chainman_opts};

Expand Down
6 changes: 6 additions & 0 deletions src/chain.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <primitives/block.h>
#include <sync.h>
#include <uint256.h>
#include <util/time.h>

#include <vector>

Expand Down Expand Up @@ -255,6 +256,11 @@ class CBlockIndex
*/
bool HaveTxsDownloaded() const { return nChainTx != 0; }

NodeSeconds Time() const
{
return NodeSeconds{std::chrono::seconds{nTime}};
}

int64_t GetBlockTime() const
{
return (int64_t)nTime;
Expand Down
13 changes: 9 additions & 4 deletions src/coinjoin/coinjoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,16 @@ bool CCoinJoinQueue::CheckSignature(const CBLSPublicKey& blsPubKey) const
return true;
}

bool CCoinJoinQueue::IsTimeOutOfBounds(int64_t current_time) const
bool CCoinJoinQueue::IsTimeOutOfBounds(NodeSeconds current_time) const
{
if (current_time < 0 || nTime < 0) return true;
return current_time - nTime > COINJOIN_QUEUE_TIMEOUT ||
nTime - current_time > COINJOIN_QUEUE_TIMEOUT;
const auto queue_time{Time()};
if (current_time < NodeSeconds{} || queue_time < NodeSeconds{}) return true;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: technically, it's valid code, but looks a bit strange without 0s

if (current_time < NodeSeconds{0} || queue_time < NodeSeconds{0}) return true;

return std::chrono::abs(current_time - queue_time) > std::chrono::seconds{COINJOIN_QUEUE_TIMEOUT};
}

bool CCoinJoinQueue::IsTimeOutOfBounds() const
{
return IsTimeOutOfBounds(std::chrono::time_point_cast<std::chrono::seconds>(GetAdjustedTime()));
}

[[nodiscard]] std::string CCoinJoinQueue::ToString() const
Expand Down
13 changes: 8 additions & 5 deletions src/coinjoin/coinjoin.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,15 +193,17 @@ class CCoinJoinQueue

CCoinJoinQueue() = default;

CCoinJoinQueue(int nDenom, const COutPoint& outpoint, const uint256& proTxHash, int64_t nTime, bool fReady) :
CCoinJoinQueue(int nDenom, const COutPoint& outpoint, const uint256& proTxHash, NodeClock::time_point time, bool fReady) :
nDenom(nDenom),
masternodeOutpoint(outpoint),
m_protxHash(proTxHash),
nTime(nTime),
nTime(TicksSinceEpoch<std::chrono::seconds>(time)),
fReady(fReady)
{
}

NodeSeconds Time() const { return NodeSeconds{std::chrono::seconds{nTime}}; }

SERIALIZE_METHODS(CCoinJoinQueue, obj)
{
READWRITE(obj.nDenom, obj.m_protxHash, obj.nTime, obj.fReady);
Expand All @@ -217,7 +219,8 @@ class CCoinJoinQueue
[[nodiscard]] bool CheckSignature(const CBLSPublicKey& blsPubKey) const;

/// Check if a queue is too old or too far into the future
[[nodiscard]] bool IsTimeOutOfBounds(int64_t current_time = GetAdjustedTime()) const;
[[nodiscard]] bool IsTimeOutOfBounds(NodeSeconds current_time) const;
[[nodiscard]] bool IsTimeOutOfBounds() const;
Comment on lines +222 to +223

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 methods with the same name is a bit strange too.

consider renaming one from IsTimeOutOfBounds to IsTimeOutOfBoundsAtTime or something

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's for the overload?


[[nodiscard]] std::string ToString() const;

Expand Down Expand Up @@ -247,11 +250,11 @@ class CCoinJoinBroadcastTx
{
}

CCoinJoinBroadcastTx(CTransactionRef _tx, const COutPoint& _outpoint, const uint256& proTxHash, int64_t _sigTime) :
CCoinJoinBroadcastTx(CTransactionRef _tx, const COutPoint& _outpoint, const uint256& proTxHash, NodeClock::time_point time) :
tx(std::move(_tx)),
masternodeOutpoint(_outpoint),
m_protxHash(proTxHash),
sigTime(_sigTime)
sigTime(TicksSinceEpoch<std::chrono::seconds>(time))
{
}

Expand Down
5 changes: 5 additions & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <uint256.h>
#include <llmq/params.h>

#include <chrono>
#include <limits>
#include <vector>

Expand Down Expand Up @@ -176,6 +177,10 @@ struct Params {
int64_t nPowTargetTimespan;
int nPowKGWHeight;
int nPowDGWHeight;
std::chrono::seconds PowTargetSpacing() const
{
return std::chrono::seconds{nPowTargetSpacing};
}
int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; }
/** The best chain should have at least this much work */
uint256 nMinimumChainWork;
Expand Down
55 changes: 26 additions & 29 deletions src/governance/governance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,25 +278,25 @@ void CGovernanceManager::CheckOrphanVotes(CGovernanceObject& govobj)
AssertLockNotHeld(cs_relay);

uint256 nHash = govobj.GetHash();
std::vector<vote_time_pair_t> vecVotePairs;
cmmapOrphanVotes.GetAll(nHash, vecVotePairs);
std::vector<governance::OrphanVote> orphan_votes;
cmmapOrphanVotes.GetAll(nHash, orphan_votes);

ScopedLockBool guard(cs_store, fRateChecksEnabled, false);

int64_t nNow = GetAdjustedTime();
const auto now{std::chrono::time_point_cast<std::chrono::seconds>(GetAdjustedTime())};
const auto tip_mn_list = m_dmnman.GetListAtChainTip();
for (const auto& pairVote : vecVotePairs) {
const auto& [vote, time] = pairVote;
for (const auto& orphan_vote : orphan_votes) {
const auto& vote = orphan_vote.vote;
bool fRemove = false;
CGovernanceException e;
if (time < nNow) {
if (orphan_vote.expiration < now) {
fRemove = true;
} else if (govobj.ProcessVote(m_mn_metaman, fRateChecksEnabled, tip_mn_list, vote, e)) {
RelayVote(vote);
fRemove = true;
}
if (fRemove) {
cmmapOrphanVotes.Erase(nHash, pairVote);
cmmapOrphanVotes.Erase(nHash, orphan_vote);
}
}
}
Expand Down Expand Up @@ -733,21 +733,21 @@ bool CGovernanceManager::MasternodeRateCheck(const CGovernanceObject& govobj, bo
}

const COutPoint& masternodeOutpoint = govobj.GetMasternodeOutpoint();
int64_t nTimestamp = govobj.GetCreationTime();
int64_t nNow = GetAdjustedTime();
int64_t nSuperblockCycleSeconds = Params().GetConsensus().nSuperblockCycle * Params().GetConsensus().nPowTargetSpacing;
const auto timestamp{govobj.CreationTime()};
const auto now{std::chrono::time_point_cast<std::chrono::seconds>(GetAdjustedTime())};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: why not just

const std::chrono::time_point_cast<std::chrono::seconds> now{(GetAdjustedTime())};

Type is already specified, auto seems useless here

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

time point cast is a function not a type

const auto superblock_cycle{Params().GetConsensus().nSuperblockCycle * Params().GetConsensus().PowTargetSpacing()};

std::string strHash = govobj.GetHash().ToString();

if (nTimestamp < nNow - 2 * nSuperblockCycleSeconds) {
if (timestamp < now - 2 * superblock_cycle) {
LogPrint(BCLog::GOBJECT, "CGovernanceManager::MasternodeRateCheck -- object %s rejected due to too old timestamp, masternode = %s, timestamp = %d, current time = %d\n",
strHash, masternodeOutpoint.ToStringShort(), nTimestamp, nNow);
strHash, masternodeOutpoint.ToStringShort(), govobj.GetCreationTime(), TicksSinceEpoch<std::chrono::seconds>(now));
return false;
}

if (nTimestamp > nNow + count_seconds(MAX_TIME_FUTURE_DEVIATION)) {
if (timestamp > now + MAX_TIME_FUTURE_DEVIATION) {
LogPrint(BCLog::GOBJECT, "CGovernanceManager::MasternodeRateCheck -- object %s rejected due to too new (future) timestamp, masternode = %s, timestamp = %d, current time = %d\n",
strHash, masternodeOutpoint.ToStringShort(), nTimestamp, nNow);
strHash, masternodeOutpoint.ToStringShort(), govobj.GetCreationTime(), TicksSinceEpoch<std::chrono::seconds>(now));
return false;
}

Expand All @@ -760,20 +760,20 @@ bool CGovernanceManager::MasternodeRateCheck(const CGovernanceObject& govobj, bo
}

// Allow 1 trigger per mn per cycle, with a small fudge factor
double dMaxRate = 2 * 1.1 / double(nSuperblockCycleSeconds);
double dMaxRate = 2 * 1.1 / static_cast<double>(count_seconds(superblock_cycle));

// Temporary copy to check rate after new timestamp is added
CRateCheckBuffer buffer = it->second.triggerBuffer;

buffer.AddTimestamp(nTimestamp);
buffer.AddTimestamp(govobj.GetCreationTime());
double dRate = buffer.GetRate();

if (dRate < dMaxRate) {
return true;
}

LogPrint(BCLog::GOBJECT, "CGovernanceManager::MasternodeRateCheck -- Rate too high: object hash = %s, masternode = %s, object timestamp = %d, rate = %f, max rate = %f\n",
strHash, masternodeOutpoint.ToStringShort(), nTimestamp, dRate, dMaxRate);
strHash, masternodeOutpoint.ToStringShort(), govobj.GetCreationTime(), dRate, dMaxRate);

if (fUpdateFailStatus) {
it->second.fStatusOK = false;
Expand Down Expand Up @@ -823,8 +823,7 @@ bool CGovernanceManager::ProcessVote(const CGovernanceVote& vote, CGovernanceExc
std::string msg{strprintf("CGovernanceManager::%s -- Unknown parent object %s, MN outpoint = %s", __func__,
nHashGovobj.ToString(), vote.GetMasternodeOutpoint().ToStringShort())};
exception = CGovernanceException(msg, GOVERNANCE_EXCEPTION_WARNING);
if (cmmapOrphanVotes.Insert(nHashGovobj, vote_time_pair_t(vote, count_seconds(GetTime<std::chrono::seconds>() +
GOVERNANCE_ORPHAN_EXPIRATION_TIME)))) {
if (cmmapOrphanVotes.Insert(nHashGovobj, governance::OrphanVote{vote, Now<NodeSeconds>() + GOVERNANCE_ORPHAN_EXPIRATION_TIME})) {
hashToRequest = nHashGovobj; // Caller should request this object
}
LogPrint(BCLog::GOBJECT, "%s\n", msg);
Expand Down Expand Up @@ -883,20 +882,19 @@ void CGovernanceManager::CheckPostponedObjects()


// Perform additional relays for triggers
int64_t nNow = GetAdjustedTime();
int64_t nSuperblockCycleSeconds = Params().GetConsensus().nSuperblockCycle * Params().GetConsensus().nPowTargetSpacing;
const auto now{std::chrono::time_point_cast<std::chrono::seconds>(GetAdjustedTime())};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: same here for auto - type is already specified on the same line

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same; time_point_cast is a function not a type

const auto superblock_cycle{Params().GetConsensus().nSuperblockCycle * Params().GetConsensus().PowTargetSpacing()};

for (auto it = setAdditionalRelayObjects.begin(); it != setAdditionalRelayObjects.end();) {
auto itObject = mapObjects.find(*it);
if (itObject != mapObjects.end()) {
const auto& govobj = *Assert(itObject->second);

int64_t nTimestamp = govobj.GetCreationTime();
const auto timestamp{govobj.CreationTime()};

bool fValid = (nTimestamp <= nNow + count_seconds(MAX_TIME_FUTURE_DEVIATION)) &&
(nTimestamp >= nNow - 2 * nSuperblockCycleSeconds);
bool fReady = (nTimestamp <=
nNow + count_seconds(MAX_TIME_FUTURE_DEVIATION) - count_seconds(RELIABLE_PROPAGATION_TIME));
const bool fValid{timestamp <= now + MAX_TIME_FUTURE_DEVIATION &&
timestamp >= now - 2 * superblock_cycle};
const bool fReady{timestamp <= now + MAX_TIME_FUTURE_DEVIATION - RELIABLE_PROPAGATION_TIME};

if (fValid) {
if (fReady) {
Expand Down Expand Up @@ -1092,15 +1090,14 @@ std::vector<uint256> CGovernanceManager::GetOrphanVoteObjectHashes()
{
LOCK(cs_store);

int64_t nNow = GetTime<std::chrono::seconds>().count();
const auto now{Now<NodeSeconds>()};

// Clean up expired orphan votes
const vote_cmm_t::list_t& items = cmmapOrphanVotes.GetItemList();
for (auto it = items.begin(); it != items.end();) {
auto prevIt = it;
++it;
const auto& [_, time] = prevIt->value;
if (time < nNow) {
if (prevIt->value.expiration < now) {
cmmapOrphanVotes.Erase(prevIt->key, prevIt->value);
}
}
Expand Down
26 changes: 22 additions & 4 deletions src/governance/governance.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@

#include <cachemap.h>
#include <cachemultimap.h>
#include <governance/vote.h>
#include <primitives/transaction.h>
#include <sync.h>
#include <util/time.h>

#include <chrono>
#include <limits>
Expand All @@ -29,7 +31,6 @@ template<typename T>
class CFlatDB;
class CGovernanceException;
class CGovernanceObject;
class CGovernanceVote;
class CInv;
class CMasternodeMetaMan;
class CMasternodeSync;
Expand All @@ -41,9 +42,26 @@ namespace governance {
class SuperblockManager;
// How long a requested governance inv hash remains in the request cache.
inline constexpr std::chrono::seconds RELIABLE_PROPAGATION_TIME{60};
} // namespace governance

using vote_time_pair_t = std::pair<CGovernanceVote, int64_t>;
struct OrphanVote {
CGovernanceVote vote;
NodeSeconds expiration;

OrphanVote() = default;
OrphanVote(const CGovernanceVote& vote, NodeSeconds expiration) : vote(vote), expiration(expiration) {}

SERIALIZE_METHODS(OrphanVote, obj)
{
// Preserve the historical integer Unix-seconds representation on disk.
READWRITE(obj.vote, Using<ChronoFormatter<int64_t>>(obj.expiration));
}
};

inline bool operator<(const OrphanVote& lhs, const OrphanVote& rhs)
{
return lhs.vote < rhs.vote;
}
} // namespace governance

static constexpr int RATE_BUFFER_SIZE = 5;
static constexpr bool DEFAULT_GOVERNANCE_ENABLE{true};
Expand Down Expand Up @@ -160,7 +178,7 @@ class GovernanceStore
};

using txout_m_t = std::map<COutPoint, last_object_rec>;
using vote_cmm_t = CacheMultiMap<uint256, vote_time_pair_t>;
using vote_cmm_t = CacheMultiMap<uint256, governance::OrphanVote>;

protected:
static constexpr int MAX_CACHE_SIZE = 1000000;
Expand Down
25 changes: 16 additions & 9 deletions src/governance/object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ bool ValidateStartEndEpoch(const UniValue& objJSON, bool fCheckExpiration, std::
return false;
}

if (fCheckExpiration && nEndEpoch <= GetAdjustedTime()) {
const auto now{std::chrono::time_point_cast<std::chrono::seconds>(GetAdjustedTime())};
if (fCheckExpiration && NodeSeconds{std::chrono::seconds{nEndEpoch}} <= now) {
strErrorMessages += "expired;";
return false;
}
Expand Down Expand Up @@ -346,6 +347,12 @@ CGovernanceObject::CGovernanceObject(const uint256& nHashParentIn, int nRevision
LoadData();
}

CGovernanceObject::CGovernanceObject(const uint256& nHashParentIn, int nRevisionIn, NodeClock::time_point time,
const uint256& nCollateralHashIn, const std::string& strDataHexIn) :
CGovernanceObject{nHashParentIn, nRevisionIn, TicksSinceEpoch<std::chrono::seconds>(time), nCollateralHashIn, strDataHexIn}
{
}

CGovernanceObject::CGovernanceObject(const CGovernanceObject& other) :
cs(),
m_obj{other.m_obj},
Expand Down Expand Up @@ -430,19 +437,19 @@ bool CGovernanceObject::ProcessVote(CMasternodeMetaMan& mn_metaman, bool fRateCh
LogPrint(BCLog::GOBJECT, "%s\n", msg);
}

int64_t nNow = GetAdjustedTime();
int64_t nVoteTimeUpdate = voteInstanceRef.nTime;
auto vote_time_update{voteInstanceRef.last_update};
if (fRateChecksEnabled) {
int64_t nTimeDelta = nNow - voteInstanceRef.nTime;
if (nTimeDelta < GOVERNANCE_UPDATE_MIN) {
const auto now{std::chrono::time_point_cast<std::chrono::seconds>(GetAdjustedTime())};
if (voteInstanceRef.last_update > now - GOVERNANCE_UPDATE_MIN) {
std::string msg{strprintf("CGovernanceObject::%s -- Masternode voting too often, MN outpoint = %s, "
"governance object hash = %s, time delta = %d",
__func__, vote.GetMasternodeOutpoint().ToStringShort(), GetHash().ToString(), nTimeDelta)};
"governance object hash = %s, last update = %d, current time = %d",
__func__, vote.GetMasternodeOutpoint().ToStringShort(), GetHash().ToString(),
TicksSinceEpoch<std::chrono::seconds>(voteInstanceRef.last_update), TicksSinceEpoch<std::chrono::seconds>(now))};
LogPrint(BCLog::GOBJECT, "%s\n", msg);
exception = CGovernanceException(msg, GOVERNANCE_EXCEPTION_TEMPORARY_ERROR);
return false;
}
nVoteTimeUpdate = nNow;
vote_time_update = std::chrono::time_point_cast<std::chrono::seconds>(now);
}

bool onlyVotingKeyAllowed = m_obj.type == GovernanceObject::PROPOSAL && vote.GetSignal() == VOTE_SIGNAL_FUNDING;
Expand All @@ -459,7 +466,7 @@ bool CGovernanceObject::ProcessVote(CMasternodeMetaMan& mn_metaman, bool fRateCh

mn_metaman.AddGovernanceVote(dmn->proTxHash, vote.GetParentHash());

voteInstanceRef = vote_instance_t(vote.GetOutcome(), nVoteTimeUpdate, vote.GetTimestamp());
voteInstanceRef = vote_instance_t(vote.GetOutcome(), vote_time_update, vote.GetTimestamp());
fileVotes.AddVote(vote);
fDirtyCache = true;
// SEND NOTIFICATION TO SCRIPT/ZMQ
Expand Down
Loading
Loading