LCOV - code coverage report
Current view: top level - src - spork.h (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 2 26 7.7 %
Date: 2026-06-25 07:23:51 Functions: 5 30 16.7 %

          Line data    Source code
       1             : // Copyright (c) 2014-2025 The Dash Core developers
       2             : // Distributed under the MIT/X11 software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #ifndef BITCOIN_SPORK_H
       6             : #define BITCOIN_SPORK_H
       7             : 
       8             : #include <hash.h>
       9             : #include <key.h>
      10             : #include <pubkey.h>
      11             : #include <saltedhasher.h>
      12             : #include <sync.h>
      13             : 
      14             : #include <array>
      15             : #include <optional>
      16             : #include <string_view>
      17             : #include <unordered_map>
      18             : #include <vector>
      19             : 
      20             : template<typename T>
      21             : class CFlatDB;
      22             : class CDataStream;
      23             : class uint256;
      24             : class CInv;
      25             : 
      26             : class CSporkMessage;
      27             : class CSporkManager;
      28             : 
      29             : /*
      30             :     Don't ever reuse these IDs for other sporks
      31             :     - This would result in old clients getting confused about which spork is for what
      32             : */
      33             : enum SporkId : int32_t {
      34             :     SPORK_2_INSTANTSEND_ENABLED                            = 10001,
      35             :     SPORK_17_QUORUM_DKG_ENABLED                            = 10016,
      36             :     SPORK_19_CHAINLOCKS_ENABLED                            = 10018,
      37             :     SPORK_21_QUORUM_ALL_CONNECTED                          = 10020,
      38             :     SPORK_23_QUORUM_POSE                                   = 10022,
      39             :     // SPORK_24_DEPRECATED = 10023,
      40             : 
      41             :     SPORK_INVALID                                          = -1,
      42             : };
      43             : template<> struct is_serializable_enum<SporkId> : std::true_type {};
      44             : 
      45             : namespace std
      46             : {
      47             :     template<> struct hash<SporkId>
      48             :     {
      49      990471 :         std::size_t operator()(SporkId const& id) const noexcept
      50             :         {
      51      990471 :             return std::hash<int>{}(id);
      52             :         }
      53             :     };
      54             : }
      55             : 
      56             : using SporkValue = int64_t;
      57             : struct CSporkDef
      58             : {
      59             :     SporkId sporkId{SPORK_INVALID};
      60             :     SporkValue defaultValue{0};
      61             :     std::string_view name;
      62             : };
      63             : 
      64             : #define MAKE_SPORK_DEF(name, defaultValue) CSporkDef{name, defaultValue, #name}
      65             : [[maybe_unused]] static constexpr std::array<CSporkDef, 5> sporkDefs = {
      66             :     MAKE_SPORK_DEF(SPORK_2_INSTANTSEND_ENABLED,            4070908800ULL), // OFF
      67             :     MAKE_SPORK_DEF(SPORK_17_QUORUM_DKG_ENABLED,            4070908800ULL), // OFF
      68             :     MAKE_SPORK_DEF(SPORK_19_CHAINLOCKS_ENABLED,            4070908800ULL), // OFF
      69             :     MAKE_SPORK_DEF(SPORK_21_QUORUM_ALL_CONNECTED,          4070908800ULL), // OFF
      70             :     MAKE_SPORK_DEF(SPORK_23_QUORUM_POSE,                   4070908800ULL), // OFF
      71             : };
      72             : #undef MAKE_SPORK_DEF
      73             : 
      74             : /**
      75             :  * Sporks are network parameters used primarily to prevent forking and turn
      76             :  * on/off certain features. They are a soft consensus mechanism.
      77             :  *
      78             :  * We use 2 main classes to manage the spork system.
      79             :  *
      80             :  * SporkMessages - low-level constructs which contain the sporkID, value,
      81             :  *                 signature and a signature timestamp
      82             :  * SporkManager  - a higher-level construct which manages the naming, use of
      83             :  *                 sporks, signatures and verification, and which sporks are active according
      84             :  *                 to this node
      85             :  */
      86             : 
      87             : /**
      88             :  * CSporkMessage is a low-level class used to encapsulate Spork messages and
      89             :  * serialize them for transmission to other peers. This includes the internal
      90             :  * spork ID, value, spork signature and timestamp for the signature.
      91             :  */
      92             : class CSporkMessage
      93             : {
      94             : private:
      95             :     std::vector<unsigned char> vchSig;
      96             : 
      97             : public:
      98           0 :     SporkId nSporkID{0};
      99           0 :     SporkValue nValue{0};
     100           0 :     int64_t nTimeSigned{0};
     101             : 
     102           0 :     CSporkMessage(SporkId nSporkID, SporkValue nValue, int64_t nTimeSigned) :
     103           0 :         nSporkID(nSporkID),
     104           0 :         nValue(nValue),
     105           0 :         nTimeSigned(nTimeSigned)
     106           0 :         {}
     107             : 
     108           0 :     CSporkMessage() = default;
     109             : 
     110           0 :     SERIALIZE_METHODS(CSporkMessage, obj)
     111             :     {
     112           0 :         READWRITE(obj.nSporkID, obj.nValue, obj.nTimeSigned, obj.vchSig);
     113           0 :     }
     114             : 
     115             :     /**
     116             :      * GetHash returns the double-sha256 hash of the serialized spork message.
     117             :      */
     118             :     uint256 GetHash() const;
     119             : 
     120             :     /**
     121             :      * GetSignatureHash returns the hash of the serialized spork message
     122             :      * without the signature included. The intent of this method is to get the
     123             :      * hash to be signed.
     124             :      */
     125             :     uint256 GetSignatureHash() const;
     126             : 
     127             :     /**
     128             :      * Sign will sign the spork message with the given key.
     129             :      */
     130             :     bool Sign(const CKey& key);
     131             : 
     132             :     /**
     133             :      * CheckSignature will ensure the spork signature matches the provided public
     134             :      * key hash.
     135             :      */
     136             :     bool CheckSignature(const CKeyID& pubKeyId) const;
     137             : 
     138             :     /**
     139             :      * GetSignerKeyID is used to recover the spork address of the key used to
     140             :      * sign this spork message.
     141             :      *
     142             :      * This method was introduced along with the multi-signer sporks feature,
     143             :      * in order to identify which spork key signed this message.
     144             :      */
     145             :     std::optional<CKeyID> GetSignerKeyID() const;
     146             : };
     147             : 
     148             : class SporkStore
     149             : {
     150             : protected:
     151             :     static const std::string SERIALIZATION_VERSION_STRING;
     152             : 
     153             :     mutable Mutex cs;
     154             : 
     155             :     Uint256HashMap<CSporkMessage> mapSporksByHash GUARDED_BY(cs);
     156             :     std::unordered_map<SporkId, std::map<CKeyID, CSporkMessage> > mapSporksActive GUARDED_BY(cs);
     157             : 
     158             : public:
     159             :     template<typename Stream>
     160           0 :     void Serialize(Stream &s) const EXCLUSIVE_LOCKS_REQUIRED(!cs)
     161             :     {
     162             :         // We don't serialize pubkey ids because pubkeys should be
     163             :         // hardcoded or be set with cmdline or options, should
     164             :         // not reuse pubkeys from previous dashd run.
     165             :         // We don't serialize private key to prevent its leakage.
     166           0 :         LOCK(cs);
     167           0 :         s << SERIALIZATION_VERSION_STRING << mapSporksByHash << mapSporksActive;
     168           0 :     }
     169             : 
     170             :     template<typename Stream>
     171           0 :     void Unserialize(Stream &s) EXCLUSIVE_LOCKS_REQUIRED(!cs)
     172             :     {
     173           0 :         LOCK(cs);
     174           0 :         std::string strVersion;
     175           0 :         s >> strVersion;
     176           0 :         if (strVersion != SERIALIZATION_VERSION_STRING) {
     177           0 :             return;
     178             :         }
     179           0 :         s >> mapSporksByHash >> mapSporksActive;
     180           0 :     }
     181             : 
     182             :     /**
     183             :      * Clear is used to clear all in-memory active spork messages. Since spork
     184             :      * public and private keys are set in init.cpp, we do not clear them here.
     185             :      *
     186             :      * This method was introduced along with the spork cache.
     187             :      */
     188             :     void Clear() EXCLUSIVE_LOCKS_REQUIRED(!cs);
     189             : 
     190             :     /**
     191             :      * ToString returns the string representation of the SporkManager.
     192             :      */
     193             :     std::string ToString() const EXCLUSIVE_LOCKS_REQUIRED(!cs);
     194             : };
     195             : 
     196             : /**
     197             :  * CSporkManager is a higher-level class which manages the node's spork
     198             :  * messages, rules for which sporks should be considered active/inactive, and
     199             :  * processing for certain sporks (e.g. spork 12).
     200             :  */
     201             : class CSporkManager : public SporkStore
     202             : {
     203             : private:
     204             :     using db_type = CFlatDB<SporkStore>;
     205             : 
     206             : private:
     207             :     const std::unique_ptr<db_type> m_db;
     208             :     bool is_valid{false};
     209             : 
     210             :     // TODO: drop mutex cs_cache completely so far as sporks are used on testnet only
     211             :     // and simplify IsSporkActive to avoid any mutex for better mainnet performance
     212             :     mutable Mutex cs_cache;
     213             :     mutable std::unordered_map<SporkId, bool> mapSporksCachedActive GUARDED_BY(cs_cache);
     214             :     mutable std::unordered_map<SporkId, SporkValue> mapSporksCachedValues GUARDED_BY(cs_cache);
     215             : 
     216             :     std::set<CKeyID> setSporkPubKeyIDs GUARDED_BY(cs);
     217             :     int nMinSporkKeys GUARDED_BY(cs) {std::numeric_limits<int>::max()};
     218             :     CKey sporkPrivKey GUARDED_BY(cs);
     219             : 
     220             :     /**
     221             :      * SporkValueIfActive is used to get the value agreed upon by the majority
     222             :      * of signed spork messages for a given Spork ID.
     223             :      */
     224             :     std::optional<SporkValue> SporkValueIfActive(SporkId nSporkID) const EXCLUSIVE_LOCKS_REQUIRED(cs, !cs_cache);
     225             : 
     226             : public:
     227             :     CSporkManager(const CSporkManager&) = delete;
     228             :     CSporkManager& operator=(const CSporkManager&) = delete;
     229             :     CSporkManager();
     230             :     ~CSporkManager();
     231             : 
     232             :     bool LoadCache();
     233             : 
     234             :     bool IsValid() const { return is_valid; }
     235             : 
     236             :     /**
     237             :      * CheckAndRemove is defined to fulfill an interface as part of the on-disk
     238             :      * cache used to cache sporks between runs. If sporks that are restored
     239             :      * from cache do not have valid signatures when compared against the
     240             :      * current spork private keys, they are removed from in-memory storage.
     241             :      *
     242             :      * This method was introduced along with the spork cache.
     243             :      */
     244             :     void CheckAndRemove() EXCLUSIVE_LOCKS_REQUIRED(!cs);
     245             : 
     246             :     /**
     247             :      * GetValidSporkSigner validates signed time and recovers the signer pubkey.
     248             :      * Returns the signer's CKeyID on success, or std::nullopt if the spork is invalid
     249             :      * (peer should be punished in that case).
     250             :      */
     251             :     [[nodiscard]] std::optional<CKeyID> GetValidSporkSigner(const CSporkMessage& spork) const
     252             :         EXCLUSIVE_LOCKS_REQUIRED(!cs);
     253             :     /**
     254             :      * ProcessSpork adds the spork to local state. Returns true if the spork was new or
     255             :      * updated and should be relayed. `keyIDSigner` must be the signer key previously
     256             :      * recovered via GetValidSporkSigner. `peer_log_suffix` is appended to log lines for
     257             :      * cross-referencing with the source peer (e.g. " peer=42").
     258             :      */
     259             :     [[nodiscard]] bool ProcessSpork(const CSporkMessage& spork, const CKeyID& keyIDSigner,
     260             :                                     std::string_view peer_log_suffix = {})
     261             :         EXCLUSIVE_LOCKS_REQUIRED(!cs, !cs_cache);
     262             : 
     263             :     /**
     264             :      * ActiveSporks returns a snapshot of currently active sporks indexed by SporkId then
     265             :      * signer CKeyID. Used by net_processing to answer the 'getsporks' p2p message.
     266             :      */
     267             :     std::unordered_map<SporkId, std::map<CKeyID, CSporkMessage>> ActiveSporks() const EXCLUSIVE_LOCKS_REQUIRED(!cs);
     268             : 
     269             :     /**
     270             :      * UpdateSpork is used by the spork RPC command to set a new spork value, sign
     271             :      * and return the spork message, ready for network relay.
     272             :      * It returns nullopt if nothing to relay
     273             :      */
     274             :     std::optional<CInv> UpdateSpork(SporkId nSporkID, SporkValue nValue) EXCLUSIVE_LOCKS_REQUIRED(!cs, !cs_cache);
     275             : 
     276             :     /**
     277             :      * IsSporkActive returns a bool for time-based sporks, and should be used
     278             :      * to determine whether the spork can be considered active or not.
     279             :      * For value-based sporks such as SPORK_5_INSTANTSEND_MAX_VALUE, the spork
     280             :      * value should not be considered a timestamp, but an integer value
     281             :      * instead, and therefore this method doesn't make sense and should not be
     282             :      * used.
     283             :      */
     284             :     bool IsSporkActive(SporkId nSporkID) const EXCLUSIVE_LOCKS_REQUIRED(!cs_cache);
     285             : 
     286             :     /**
     287             :      * GetSporkValue returns the spork value given a Spork ID. If no active spork
     288             :      * message has yet been received by the node, it returns the default value.
     289             :      */
     290             :     SporkValue GetSporkValue(SporkId nSporkID) const EXCLUSIVE_LOCKS_REQUIRED(!cs, !cs_cache);
     291             : 
     292             :     /**
     293             :      * GetSporkIDByName returns the internal Spork ID given the spork name.
     294             :      */
     295             :     static SporkId GetSporkIDByName(std::string_view strName);
     296             : 
     297             :     /**
     298             :      * GetSporkByHash returns a spork message given a hash of the spork message.
     299             :      *
     300             :      * This is used when a requesting peer sends a MSG_SPORK inventory message with
     301             :      * the hash, to quickly lookup and return the full spork message. We maintain a
     302             :      * hash-based index of sporks for this reason, and this function is the access
     303             :      * point into that index.
     304             :      */
     305             :     std::optional<CSporkMessage> GetSporkByHash(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
     306             : 
     307             :     /**
     308             :      * SetSporkAddress is used to set a public key ID which will be used to
     309             :      * verify spork signatures.
     310             :      *
     311             :      * This can be called multiple times to add multiple keys to the set of
     312             :      * valid spork signers.
     313             :      */
     314             :     bool SetSporkAddress(const std::string& strAddress) EXCLUSIVE_LOCKS_REQUIRED(!cs);
     315             : 
     316             :     /**
     317             :      * SetMinSporkKeys is used to set the required spork signer threshold, for
     318             :      * a spork to be considered active.
     319             :      *
     320             :      * This value must be at least a majority of the total number of spork
     321             :      * keys, and for obvious reasons cannot be larger than that number.
     322             :      */
     323             :     bool SetMinSporkKeys(int minSporkKeys) EXCLUSIVE_LOCKS_REQUIRED(!cs);
     324             : 
     325             :     /**
     326             :      * SetPrivKey is used to set a spork key to enable setting / signing of
     327             :      * spork values.
     328             :      *
     329             :      * This will return false if the private key does not match any spork
     330             :      * address in the set of valid spork signers (see SetSporkAddress).
     331             :      */
     332             :     bool SetPrivKey(const std::string& strPrivKey) EXCLUSIVE_LOCKS_REQUIRED(!cs);
     333             : };
     334             : 
     335             : #endif // BITCOIN_SPORK_H

Generated by: LCOV version 1.16