diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -963,6 +963,7 @@ avalanche/proof.cpp avalanche/proofid.cpp avalanche/proofpool.cpp + avalanche/stakecontendercache.cpp avalanche/voterecord.cpp cashaddr.cpp # via cashaddrenc.cpp cashaddrenc.cpp # via key_io.cpp diff --git a/src/avalanche/processor.h b/src/avalanche/processor.h --- a/src/avalanche/processor.h +++ b/src/avalanche/processor.h @@ -10,6 +10,7 @@ #include <avalanche/proof.h> #include <avalanche/proofcomparator.h> #include <avalanche/protocol.h> +#include <avalanche/stakecontendercache.h> #include <avalanche/voterecord.h> // For AVALANCHE_MAX_INFLIGHT_POLL #include <blockindex.h> #include <blockindexcomparators.h> @@ -244,6 +245,9 @@ std::unordered_map<BlockHash, StakingReward, SaltedUint256Hasher> stakingRewards GUARDED_BY(cs_stakingRewards); + mutable Mutex cs_stakeContenderCache; + StakeContenderCache stakeContenderCache GUARDED_BY(cs_stakeContenderCache); + Processor(Config avaconfig, interfaces::Chain &chain, CConnman *connmanIn, ChainstateManager &chainman, CTxMemPool *mempoolIn, CScheduler &scheduler, std::unique_ptr<PeerData> peerDataIn, @@ -366,6 +370,12 @@ const CNode &node) override LOCKS_EXCLUDED(cs_main) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager, !cs_delayedAvahelloNodeIds); + /** Track votes on stake contenders */ + void addStakeContender(const ProofRef &proof) + EXCLUSIVE_LOCKS_REQUIRED(cs_main, !cs_stakeContenderCache); + int getStakeContenderStatus(const StakeContenderId &contenderId) const + EXCLUSIVE_LOCKS_REQUIRED(!cs_stakeContenderCache); + private: void updatedBlockTip() EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager, !cs_finalizedItems); diff --git a/src/avalanche/processor.cpp b/src/avalanche/processor.cpp --- a/src/avalanche/processor.cpp +++ b/src/avalanche/processor.cpp @@ -970,6 +970,19 @@ WITH_LOCK(cs_delayedAvahelloNodeIds, delayedAvahelloNodeIds.erase(nodeid)); } +void Processor::addStakeContender(const ProofRef &proof) { + AssertLockHeld(cs_main); + const CBlockIndex *activeTip = chainman.ActiveTip(); + WITH_LOCK(cs_stakeContenderCache, + return stakeContenderCache.add(activeTip, proof)); +} + +int Processor::getStakeContenderStatus( + const StakeContenderId &contenderId) const { + return WITH_LOCK(cs_stakeContenderCache, + return stakeContenderCache.getVoteStatus(contenderId)); +} + void Processor::updatedBlockTip() { const bool registerLocalProof = canShareLocalProof(); auto registerProofs = [&]() { diff --git a/src/avalanche/test/processor_tests.cpp b/src/avalanche/test/processor_tests.cpp --- a/src/avalanche/test/processor_tests.cpp +++ b/src/avalanche/test/processor_tests.cpp @@ -2391,4 +2391,24 @@ BOOST_CHECK(m_processor->reconcileOrFinalize(betterProof)); } +BOOST_AUTO_TEST_CASE(stake_contenders) { + ChainstateManager &chainman = *Assert(m_node.chainman); + Chainstate &active_chainstate = chainman.ActiveChainstate(); + const CBlockIndex *chaintip = + WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip()); + + auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE); + StakeContenderId contenderId(chaintip->GetBlockHash(), proof->getId()); + + // Stake contender isn't in the cache yet + BOOST_CHECK_EQUAL(m_processor->getStakeContenderStatus(contenderId), -1); + + // Add stake contender to the cache. It defaults to rejected. + { + LOCK(cs_main); + m_processor->addStakeContender(proof); + } + BOOST_CHECK_EQUAL(m_processor->getStakeContenderStatus(contenderId), 1); +} + BOOST_AUTO_TEST_SUITE_END()