2022-06-08 01:36:46 +02:00
|
|
|
// Copyright (c) 2014-2022 The Dash Core developers
|
2015-02-09 21:54:51 +01:00
|
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
2016-07-30 13:04:27 +02:00
|
|
|
|
2018-04-02 00:30:17 +02:00
|
|
|
#ifndef BITCOIN_SPORK_H
|
|
|
|
#define BITCOIN_SPORK_H
|
2015-02-09 21:54:51 +01:00
|
|
|
|
2020-03-19 23:46:56 +01:00
|
|
|
#include <hash.h>
|
2021-10-25 15:55:34 +02:00
|
|
|
#include <key.h>
|
2020-03-19 23:46:56 +01:00
|
|
|
#include <net.h>
|
2023-12-18 13:15:06 +01:00
|
|
|
#include <net_types.h>
|
2021-10-25 15:55:34 +02:00
|
|
|
#include <pubkey.h>
|
2018-11-30 18:21:03 +01:00
|
|
|
#include <saltedhasher.h>
|
2021-10-25 15:55:34 +02:00
|
|
|
#include <sync.h>
|
|
|
|
#include <uint256.h>
|
2015-04-03 00:51:08 +02:00
|
|
|
|
2021-10-25 15:55:34 +02:00
|
|
|
#include <array>
|
2022-10-19 20:37:28 +02:00
|
|
|
#include <optional>
|
2021-10-25 15:55:34 +02:00
|
|
|
#include <string_view>
|
Collection of minor performance optimizations (#2855)
* Merge #13176: Improve CRollingBloomFilter performance: replace modulus with FastMod
9aac9f90d5e56752cc6cbfac48063ad29a01143c replace modulus with FastMod (Martin Ankerl)
Pull request description:
Not sure if this is optimization is necessary, but anyway I have some spare time so here it is. This replaces the slow modulo operation with a much faster 64bit multiplication & shift. This works when the hash is uniformly distributed between 0 and 2^32-1. This speeds up the benchmark by a factor of about 1.3:
```
RollingBloom, 5, 1500000, 3.73733, 4.97569e-07, 4.99002e-07, 4.98372e-07 # before
RollingBloom, 5, 1500000, 2.86842, 3.81630e-07, 3.83730e-07, 3.82473e-07 # FastMod
```
Be aware that this changes the internal data of the filter, so this should probably
not be used for CBloomFilter because of interoperability problems.
Tree-SHA512: 04104f3fb09f56c9d14458a6aad919aeb0a5af944e8ee6a31f00e93c753e22004648c1cd65bf36752b6addec528d19fb665c27b955ce1666a85a928e17afa47a
* Use unordered_map in CSporkManager
In one of my profiling sessions with many InstantSend transactions
happening, calls into CSporkManager added up to about 1% of total CPU time.
This is easily avoidable by using unordered maps.
* Use std::unordered_map instead of std::map in limitedmap
* Use unordered_set for CNode::setAskFor
* Add serialization support for unordered maps and sets
* Use unordered_map for mapArgs and mapMultiArgs
* Let limitedmap prune in batches and use unordered_multimap
Due to the batched pruning, there is no need to maintain an ordered map
of values anymore. Only when nPruneAfterSize, there is a need to create
a temporary ordered vector of values to figure out what can be removed.
* Instead of using a multimap for mapAskFor, use a vector which we sort on demand
CNode::AskFor will now push entries into an initially unordered vector
instead of an ordered multimap. Only when we later want to use vecAskFor in
SendMessages, we sort the vector.
The vector will actually be mostly sorted in most cases as insertion order
usually mimics the desired ordering. Only the last few entries might need
some shuffling around. Doing the sort on-demand should be less wasteful
then trying to maintain correct order all the time.
* Fix compilation of tests
* Fix limitedmap tests
* Rename limitedmap to unordered_limitedmap to ensure backports conflict
This ensures that future backports that depends on limitedmap's ordering
conflict so that we are made aware of needed action.
* Fix compilation error on Travis
2019-04-11 14:42:14 +02:00
|
|
|
#include <unordered_map>
|
2021-10-25 15:55:34 +02:00
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
class CConnman;
|
refactor: decouple db hooks from CFlatDB-based C*Manager objects, migrate to *Store structs (#5555)
## Motivation
As highlighted in https://github.com/dashpay/dash-issues/issues/52,
decoupling of `CFlatDB`-interacting components from managers of objects
like `CGovernanceManager` and `CSporkManager` is a key task for
achieving deglobalization of Dash-specific components.
The design of `CFlatDB` as a flat database agent relies on hooking into
the object's state its meant to load and store, using its
(de)serialization routines and other miscellaneous functions (notably,
without defining an interface) to achieve those ends. This approach was
taken predominantly for components that want a single-file cache.
Because of the method it uses to hook into the object (templates and the
use of temporary objects), it explicitly prevented passing arguments
into the object constructor, an explicit requirement for storing
references to other components during construction. This, in turn,
created an explicit dependency on those same components being available
in the global context, which would block the backport of bitcoin#21866,
a requirement for future backports meant to achieve parity in
`assumeutxo` support.
The design of these objects made no separation between persistent (i.e.
cached) and ephemeral (i.e. generated/fetched during initialization or
state transitions) data and the design of `CFlatDB` attempts to "clean"
the database by breaching this separation and attempting to access this
ephemeral data.
This might be acceptable if it is contained within the manager itself,
like `CSporkManager`'s `CheckAndRemove()` but is utterly unacceptable
when it relies on other managers (that, as a reminder, are only
accessible through the global state because of restrictions caused by
existing design), like `CGovernanceManager`'s `UpdateCachesAndClean()`.
This pull request aims to separate the `CFlatDB`-interacting portions of
these managers into a struct, with `CFlatDB` interacting only with this
struct, while the manager inherits the struct and manages
load/store/update of the database through the `CFlatDB` instance
initialized within its scope, though the instance only has knowledge of
what is exposed through the limited parent struct.
## Additional information
* As regards to existing behaviour, `CFlatDB` is written entirely as a
header as it relies on templates to specialize itself for the object it
hooks into. Attempting to split the logic and function definitions into
separate files will require you to explicitly define template
specializations, which is tedious.
* `m_db` is defined as a pointer as you cannot instantiate a
forward-declared template (see [this Stack Overflow
answer](https://stackoverflow.com/a/12797282) for more information),
which is done when defined as a member in the object scope.
* The conditional cache flush predicating on RPC _not_ being in the
warm-up state has been replaced with unconditional flushing of the
database on object destruction (@UdjinM6, is this acceptable?)
## TODOs
This is a list of things that aren't within the scope of this pull
request but should be addressed in subsequent pull requests
* [ ] Definition of an interface that `CFlatDB` stores are expected to
implement
* [ ] Lock annotations for all potential uses of members protected by
the `cs` mutex in each manager object and store
* [ ] Additional comments documenting what each function and member does
* [ ] Deglobalization of affected managers
---------
Co-authored-by: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com>
2023-09-24 16:50:21 +02:00
|
|
|
template<typename T>
|
|
|
|
class CFlatDB;
|
2021-10-25 15:55:34 +02:00
|
|
|
class CNode;
|
|
|
|
class CDataStream;
|
Collection of minor performance optimizations (#2855)
* Merge #13176: Improve CRollingBloomFilter performance: replace modulus with FastMod
9aac9f90d5e56752cc6cbfac48063ad29a01143c replace modulus with FastMod (Martin Ankerl)
Pull request description:
Not sure if this is optimization is necessary, but anyway I have some spare time so here it is. This replaces the slow modulo operation with a much faster 64bit multiplication & shift. This works when the hash is uniformly distributed between 0 and 2^32-1. This speeds up the benchmark by a factor of about 1.3:
```
RollingBloom, 5, 1500000, 3.73733, 4.97569e-07, 4.99002e-07, 4.98372e-07 # before
RollingBloom, 5, 1500000, 2.86842, 3.81630e-07, 3.83730e-07, 3.82473e-07 # FastMod
```
Be aware that this changes the internal data of the filter, so this should probably
not be used for CBloomFilter because of interoperability problems.
Tree-SHA512: 04104f3fb09f56c9d14458a6aad919aeb0a5af944e8ee6a31f00e93c753e22004648c1cd65bf36752b6addec528d19fb665c27b955ce1666a85a928e17afa47a
* Use unordered_map in CSporkManager
In one of my profiling sessions with many InstantSend transactions
happening, calls into CSporkManager added up to about 1% of total CPU time.
This is easily avoidable by using unordered maps.
* Use std::unordered_map instead of std::map in limitedmap
* Use unordered_set for CNode::setAskFor
* Add serialization support for unordered maps and sets
* Use unordered_map for mapArgs and mapMultiArgs
* Let limitedmap prune in batches and use unordered_multimap
Due to the batched pruning, there is no need to maintain an ordered map
of values anymore. Only when nPruneAfterSize, there is a need to create
a temporary ordered vector of values to figure out what can be removed.
* Instead of using a multimap for mapAskFor, use a vector which we sort on demand
CNode::AskFor will now push entries into an initially unordered vector
instead of an ordered multimap. Only when we later want to use vecAskFor in
SendMessages, we sort the vector.
The vector will actually be mostly sorted in most cases as insertion order
usually mimics the desired ordering. Only the last few entries might need
some shuffling around. Doing the sort on-demand should be less wasteful
then trying to maintain correct order all the time.
* Fix compilation of tests
* Fix limitedmap tests
* Rename limitedmap to unordered_limitedmap to ensure backports conflict
This ensures that future backports that depends on limitedmap's ordering
conflict so that we are made aware of needed action.
* Fix compilation error on Travis
2019-04-11 14:42:14 +02:00
|
|
|
|
2016-08-29 21:16:02 +02:00
|
|
|
class CSporkMessage;
|
|
|
|
class CSporkManager;
|
|
|
|
|
2015-05-30 19:27:51 +02:00
|
|
|
/*
|
|
|
|
Don't ever reuse these IDs for other sporks
|
|
|
|
- This would result in old clients getting confused about which spork is for what
|
|
|
|
*/
|
2019-05-29 20:18:31 +02:00
|
|
|
enum SporkId : int32_t {
|
|
|
|
SPORK_2_INSTANTSEND_ENABLED = 10001,
|
|
|
|
SPORK_3_INSTANTSEND_BLOCK_FILTERING = 10002,
|
|
|
|
SPORK_9_SUPERBLOCKS_ENABLED = 10008,
|
|
|
|
SPORK_17_QUORUM_DKG_ENABLED = 10016,
|
|
|
|
SPORK_19_CHAINLOCKS_ENABLED = 10018,
|
2020-03-16 11:06:38 +01:00
|
|
|
SPORK_21_QUORUM_ALL_CONNECTED = 10020,
|
2021-01-11 04:23:01 +01:00
|
|
|
SPORK_23_QUORUM_POSE = 10022,
|
2023-11-13 17:03:46 +01:00
|
|
|
SPORK_24_TEST_EHF = 10023,
|
2019-05-29 20:18:31 +02:00
|
|
|
|
|
|
|
SPORK_INVALID = -1,
|
|
|
|
};
|
|
|
|
template<> struct is_serializable_enum<SporkId> : std::true_type {};
|
|
|
|
|
2019-06-08 13:16:20 +02:00
|
|
|
namespace std
|
|
|
|
{
|
|
|
|
template<> struct hash<SporkId>
|
|
|
|
{
|
|
|
|
std::size_t operator()(SporkId const& id) const noexcept
|
|
|
|
{
|
|
|
|
return std::hash<int>{}(id);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-02-20 00:04:57 +01:00
|
|
|
using SporkValue = int64_t;
|
2019-05-29 20:18:31 +02:00
|
|
|
struct CSporkDef
|
|
|
|
{
|
|
|
|
SporkId sporkId{SPORK_INVALID};
|
2023-02-20 00:04:57 +01:00
|
|
|
SporkValue defaultValue{0};
|
2021-10-12 01:08:02 +02:00
|
|
|
std::string_view name;
|
2019-05-29 20:18:31 +02:00
|
|
|
};
|
|
|
|
|
2021-10-12 01:08:02 +02:00
|
|
|
#define MAKE_SPORK_DEF(name, defaultValue) CSporkDef{name, defaultValue, #name}
|
2023-10-18 05:31:40 +02:00
|
|
|
[[maybe_unused]] static constexpr std::array<CSporkDef, 8> sporkDefs = {
|
2021-10-25 15:55:34 +02:00
|
|
|
MAKE_SPORK_DEF(SPORK_2_INSTANTSEND_ENABLED, 4070908800ULL), // OFF
|
|
|
|
MAKE_SPORK_DEF(SPORK_3_INSTANTSEND_BLOCK_FILTERING, 4070908800ULL), // OFF
|
|
|
|
MAKE_SPORK_DEF(SPORK_9_SUPERBLOCKS_ENABLED, 4070908800ULL), // OFF
|
|
|
|
MAKE_SPORK_DEF(SPORK_17_QUORUM_DKG_ENABLED, 4070908800ULL), // OFF
|
|
|
|
MAKE_SPORK_DEF(SPORK_19_CHAINLOCKS_ENABLED, 4070908800ULL), // OFF
|
|
|
|
MAKE_SPORK_DEF(SPORK_21_QUORUM_ALL_CONNECTED, 4070908800ULL), // OFF
|
|
|
|
MAKE_SPORK_DEF(SPORK_23_QUORUM_POSE, 4070908800ULL), // OFF
|
2023-11-13 17:03:46 +01:00
|
|
|
MAKE_SPORK_DEF(SPORK_24_TEST_EHF, 4070908800ULL), // OFF
|
2021-10-12 01:08:02 +02:00
|
|
|
};
|
|
|
|
#undef MAKE_SPORK_DEF
|
2022-08-26 23:52:53 +02:00
|
|
|
extern std::unique_ptr<CSporkManager> sporkManager;
|
2015-02-09 21:54:51 +01:00
|
|
|
|
2018-10-02 11:02:27 +02:00
|
|
|
/**
|
|
|
|
* Sporks are network parameters used primarily to prevent forking and turn
|
|
|
|
* on/off certain features. They are a soft consensus mechanism.
|
|
|
|
*
|
|
|
|
* We use 2 main classes to manage the spork system.
|
|
|
|
*
|
|
|
|
* SporkMessages - low-level constructs which contain the sporkID, value,
|
|
|
|
* signature and a signature timestamp
|
|
|
|
* SporkManager - a higher-level construct which manages the naming, use of
|
|
|
|
* sporks, signatures and verification, and which sporks are active according
|
|
|
|
* to this node
|
|
|
|
*/
|
2015-02-09 21:54:51 +01:00
|
|
|
|
2018-10-02 11:02:27 +02:00
|
|
|
/**
|
|
|
|
* CSporkMessage is a low-level class used to encapsulate Spork messages and
|
|
|
|
* serialize them for transmission to other peers. This includes the internal
|
|
|
|
* spork ID, value, spork signature and timestamp for the signature.
|
|
|
|
*/
|
2015-02-09 21:54:51 +01:00
|
|
|
class CSporkMessage
|
|
|
|
{
|
2016-07-30 13:04:27 +02:00
|
|
|
private:
|
2015-02-09 21:54:51 +01:00
|
|
|
std::vector<unsigned char> vchSig;
|
2016-07-30 13:04:27 +02:00
|
|
|
|
|
|
|
public:
|
2021-12-17 18:22:11 +01:00
|
|
|
SporkId nSporkID{0};
|
2023-02-20 00:04:57 +01:00
|
|
|
SporkValue nValue{0};
|
2021-12-17 18:22:11 +01:00
|
|
|
int64_t nTimeSigned{0};
|
2015-02-09 21:54:51 +01:00
|
|
|
|
2023-02-20 00:04:57 +01:00
|
|
|
CSporkMessage(SporkId nSporkID, SporkValue nValue, int64_t nTimeSigned) :
|
2016-08-29 21:16:02 +02:00
|
|
|
nSporkID(nSporkID),
|
|
|
|
nValue(nValue),
|
|
|
|
nTimeSigned(nTimeSigned)
|
|
|
|
{}
|
2016-07-30 13:04:27 +02:00
|
|
|
|
2021-12-17 18:22:11 +01:00
|
|
|
CSporkMessage() = default;
|
2015-02-09 21:54:51 +01:00
|
|
|
|
2021-05-27 17:17:29 +02:00
|
|
|
SERIALIZE_METHODS(CSporkMessage, obj)
|
|
|
|
{
|
|
|
|
READWRITE(obj.nSporkID, obj.nValue, obj.nTimeSigned, obj.vchSig);
|
2015-04-03 00:51:08 +02:00
|
|
|
}
|
2016-08-29 21:16:02 +02:00
|
|
|
|
2018-10-02 11:02:27 +02:00
|
|
|
/**
|
|
|
|
* GetHash returns the double-sha256 hash of the serialized spork message.
|
|
|
|
*/
|
2018-02-16 15:54:53 +01:00
|
|
|
uint256 GetHash() const;
|
2018-10-02 11:02:27 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* GetSignatureHash returns the hash of the serialized spork message
|
|
|
|
* without the signature included. The intent of this method is to get the
|
|
|
|
* hash to be signed.
|
|
|
|
*/
|
2018-02-16 15:54:53 +01:00
|
|
|
uint256 GetSignatureHash() const;
|
2016-08-29 21:16:02 +02:00
|
|
|
|
2018-10-02 11:02:27 +02:00
|
|
|
/**
|
|
|
|
* Sign will sign the spork message with the given key.
|
|
|
|
*/
|
2020-08-30 16:22:21 +02:00
|
|
|
bool Sign(const CKey& key);
|
2018-10-02 11:02:27 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* CheckSignature will ensure the spork signature matches the provided public
|
|
|
|
* key hash.
|
|
|
|
*/
|
2020-08-30 16:22:21 +02:00
|
|
|
bool CheckSignature(const CKeyID& pubKeyId) const;
|
2018-10-02 11:02:27 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* GetSignerKeyID is used to recover the spork address of the key used to
|
|
|
|
* sign this spork message.
|
|
|
|
*
|
|
|
|
* This method was introduced along with the multi-signer sporks feature,
|
|
|
|
* in order to identify which spork key signed this message.
|
|
|
|
*/
|
2022-10-19 20:37:28 +02:00
|
|
|
std::optional<CKeyID> GetSignerKeyID() const;
|
2018-10-02 11:02:27 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Relay is used to send this spork message to other peers.
|
|
|
|
*/
|
2021-01-22 06:57:42 +01:00
|
|
|
void Relay(CConnman& connman) const;
|
2015-02-09 21:54:51 +01:00
|
|
|
};
|
|
|
|
|
refactor: decouple db hooks from CFlatDB-based C*Manager objects, migrate to *Store structs (#5555)
## Motivation
As highlighted in https://github.com/dashpay/dash-issues/issues/52,
decoupling of `CFlatDB`-interacting components from managers of objects
like `CGovernanceManager` and `CSporkManager` is a key task for
achieving deglobalization of Dash-specific components.
The design of `CFlatDB` as a flat database agent relies on hooking into
the object's state its meant to load and store, using its
(de)serialization routines and other miscellaneous functions (notably,
without defining an interface) to achieve those ends. This approach was
taken predominantly for components that want a single-file cache.
Because of the method it uses to hook into the object (templates and the
use of temporary objects), it explicitly prevented passing arguments
into the object constructor, an explicit requirement for storing
references to other components during construction. This, in turn,
created an explicit dependency on those same components being available
in the global context, which would block the backport of bitcoin#21866,
a requirement for future backports meant to achieve parity in
`assumeutxo` support.
The design of these objects made no separation between persistent (i.e.
cached) and ephemeral (i.e. generated/fetched during initialization or
state transitions) data and the design of `CFlatDB` attempts to "clean"
the database by breaching this separation and attempting to access this
ephemeral data.
This might be acceptable if it is contained within the manager itself,
like `CSporkManager`'s `CheckAndRemove()` but is utterly unacceptable
when it relies on other managers (that, as a reminder, are only
accessible through the global state because of restrictions caused by
existing design), like `CGovernanceManager`'s `UpdateCachesAndClean()`.
This pull request aims to separate the `CFlatDB`-interacting portions of
these managers into a struct, with `CFlatDB` interacting only with this
struct, while the manager inherits the struct and manages
load/store/update of the database through the `CFlatDB` instance
initialized within its scope, though the instance only has knowledge of
what is exposed through the limited parent struct.
## Additional information
* As regards to existing behaviour, `CFlatDB` is written entirely as a
header as it relies on templates to specialize itself for the object it
hooks into. Attempting to split the logic and function definitions into
separate files will require you to explicitly define template
specializations, which is tedious.
* `m_db` is defined as a pointer as you cannot instantiate a
forward-declared template (see [this Stack Overflow
answer](https://stackoverflow.com/a/12797282) for more information),
which is done when defined as a member in the object scope.
* The conditional cache flush predicating on RPC _not_ being in the
warm-up state has been replaced with unconditional flushing of the
database on object destruction (@UdjinM6, is this acceptable?)
## TODOs
This is a list of things that aren't within the scope of this pull
request but should be addressed in subsequent pull requests
* [ ] Definition of an interface that `CFlatDB` stores are expected to
implement
* [ ] Lock annotations for all potential uses of members protected by
the `cs` mutex in each manager object and store
* [ ] Additional comments documenting what each function and member does
* [ ] Deglobalization of affected managers
---------
Co-authored-by: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com>
2023-09-24 16:50:21 +02:00
|
|
|
class SporkStore
|
2015-02-09 21:54:51 +01:00
|
|
|
{
|
refactor: decouple db hooks from CFlatDB-based C*Manager objects, migrate to *Store structs (#5555)
## Motivation
As highlighted in https://github.com/dashpay/dash-issues/issues/52,
decoupling of `CFlatDB`-interacting components from managers of objects
like `CGovernanceManager` and `CSporkManager` is a key task for
achieving deglobalization of Dash-specific components.
The design of `CFlatDB` as a flat database agent relies on hooking into
the object's state its meant to load and store, using its
(de)serialization routines and other miscellaneous functions (notably,
without defining an interface) to achieve those ends. This approach was
taken predominantly for components that want a single-file cache.
Because of the method it uses to hook into the object (templates and the
use of temporary objects), it explicitly prevented passing arguments
into the object constructor, an explicit requirement for storing
references to other components during construction. This, in turn,
created an explicit dependency on those same components being available
in the global context, which would block the backport of bitcoin#21866,
a requirement for future backports meant to achieve parity in
`assumeutxo` support.
The design of these objects made no separation between persistent (i.e.
cached) and ephemeral (i.e. generated/fetched during initialization or
state transitions) data and the design of `CFlatDB` attempts to "clean"
the database by breaching this separation and attempting to access this
ephemeral data.
This might be acceptable if it is contained within the manager itself,
like `CSporkManager`'s `CheckAndRemove()` but is utterly unacceptable
when it relies on other managers (that, as a reminder, are only
accessible through the global state because of restrictions caused by
existing design), like `CGovernanceManager`'s `UpdateCachesAndClean()`.
This pull request aims to separate the `CFlatDB`-interacting portions of
these managers into a struct, with `CFlatDB` interacting only with this
struct, while the manager inherits the struct and manages
load/store/update of the database through the `CFlatDB` instance
initialized within its scope, though the instance only has knowledge of
what is exposed through the limited parent struct.
## Additional information
* As regards to existing behaviour, `CFlatDB` is written entirely as a
header as it relies on templates to specialize itself for the object it
hooks into. Attempting to split the logic and function definitions into
separate files will require you to explicitly define template
specializations, which is tedious.
* `m_db` is defined as a pointer as you cannot instantiate a
forward-declared template (see [this Stack Overflow
answer](https://stackoverflow.com/a/12797282) for more information),
which is done when defined as a member in the object scope.
* The conditional cache flush predicating on RPC _not_ being in the
warm-up state has been replaced with unconditional flushing of the
database on object destruction (@UdjinM6, is this acceptable?)
## TODOs
This is a list of things that aren't within the scope of this pull
request but should be addressed in subsequent pull requests
* [ ] Definition of an interface that `CFlatDB` stores are expected to
implement
* [ ] Lock annotations for all potential uses of members protected by
the `cs` mutex in each manager object and store
* [ ] Additional comments documenting what each function and member does
* [ ] Deglobalization of affected managers
---------
Co-authored-by: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com>
2023-09-24 16:50:21 +02:00
|
|
|
protected:
|
|
|
|
static const std::string SERIALIZATION_VERSION_STRING;
|
2022-04-11 17:22:05 +02:00
|
|
|
|
|
|
|
mutable Mutex cs;
|
2021-01-21 03:04:14 +01:00
|
|
|
|
2018-11-30 18:21:03 +01:00
|
|
|
std::unordered_map<uint256, CSporkMessage, StaticSaltedHasher> mapSporksByHash GUARDED_BY(cs);
|
2021-09-28 23:23:34 +02:00
|
|
|
std::unordered_map<SporkId, std::map<CKeyID, CSporkMessage> > mapSporksActive GUARDED_BY(cs);
|
2015-02-09 21:54:51 +01:00
|
|
|
|
|
|
|
public:
|
2021-05-27 17:17:29 +02:00
|
|
|
template<typename Stream>
|
2022-04-11 17:22:05 +02:00
|
|
|
void Serialize(Stream &s) const LOCKS_EXCLUDED(cs)
|
2021-05-27 17:17:29 +02:00
|
|
|
{
|
|
|
|
// We don't serialize pubkey ids because pubkeys should be
|
|
|
|
// hardcoded or be set with cmdline or options, should
|
|
|
|
// not reuse pubkeys from previous dashd run.
|
|
|
|
// We don't serialize private key to prevent its leakage.
|
|
|
|
LOCK(cs);
|
|
|
|
s << SERIALIZATION_VERSION_STRING << mapSporksByHash << mapSporksActive;
|
|
|
|
}
|
2018-08-13 22:21:21 +02:00
|
|
|
|
2021-05-27 17:17:29 +02:00
|
|
|
template<typename Stream>
|
2022-04-11 17:22:05 +02:00
|
|
|
void Unserialize(Stream &s) LOCKS_EXCLUDED(cs)
|
2021-05-27 17:17:29 +02:00
|
|
|
{
|
|
|
|
LOCK(cs);
|
2018-09-26 16:15:02 +02:00
|
|
|
std::string strVersion;
|
2021-05-27 17:17:29 +02:00
|
|
|
s >> strVersion;
|
|
|
|
if (strVersion != SERIALIZATION_VERSION_STRING) {
|
|
|
|
return;
|
2018-09-26 16:15:02 +02:00
|
|
|
}
|
2021-05-27 17:17:29 +02:00
|
|
|
s >> mapSporksByHash >> mapSporksActive;
|
2018-08-13 22:21:21 +02:00
|
|
|
}
|
|
|
|
|
2018-10-02 11:02:27 +02:00
|
|
|
/**
|
|
|
|
* Clear is used to clear all in-memory active spork messages. Since spork
|
|
|
|
* public and private keys are set in init.cpp, we do not clear them here.
|
|
|
|
*
|
|
|
|
* This method was introduced along with the spork cache.
|
|
|
|
*/
|
2022-04-11 17:22:05 +02:00
|
|
|
void Clear() LOCKS_EXCLUDED(cs);
|
2018-10-02 11:02:27 +02:00
|
|
|
|
refactor: decouple db hooks from CFlatDB-based C*Manager objects, migrate to *Store structs (#5555)
## Motivation
As highlighted in https://github.com/dashpay/dash-issues/issues/52,
decoupling of `CFlatDB`-interacting components from managers of objects
like `CGovernanceManager` and `CSporkManager` is a key task for
achieving deglobalization of Dash-specific components.
The design of `CFlatDB` as a flat database agent relies on hooking into
the object's state its meant to load and store, using its
(de)serialization routines and other miscellaneous functions (notably,
without defining an interface) to achieve those ends. This approach was
taken predominantly for components that want a single-file cache.
Because of the method it uses to hook into the object (templates and the
use of temporary objects), it explicitly prevented passing arguments
into the object constructor, an explicit requirement for storing
references to other components during construction. This, in turn,
created an explicit dependency on those same components being available
in the global context, which would block the backport of bitcoin#21866,
a requirement for future backports meant to achieve parity in
`assumeutxo` support.
The design of these objects made no separation between persistent (i.e.
cached) and ephemeral (i.e. generated/fetched during initialization or
state transitions) data and the design of `CFlatDB` attempts to "clean"
the database by breaching this separation and attempting to access this
ephemeral data.
This might be acceptable if it is contained within the manager itself,
like `CSporkManager`'s `CheckAndRemove()` but is utterly unacceptable
when it relies on other managers (that, as a reminder, are only
accessible through the global state because of restrictions caused by
existing design), like `CGovernanceManager`'s `UpdateCachesAndClean()`.
This pull request aims to separate the `CFlatDB`-interacting portions of
these managers into a struct, with `CFlatDB` interacting only with this
struct, while the manager inherits the struct and manages
load/store/update of the database through the `CFlatDB` instance
initialized within its scope, though the instance only has knowledge of
what is exposed through the limited parent struct.
## Additional information
* As regards to existing behaviour, `CFlatDB` is written entirely as a
header as it relies on templates to specialize itself for the object it
hooks into. Attempting to split the logic and function definitions into
separate files will require you to explicitly define template
specializations, which is tedious.
* `m_db` is defined as a pointer as you cannot instantiate a
forward-declared template (see [this Stack Overflow
answer](https://stackoverflow.com/a/12797282) for more information),
which is done when defined as a member in the object scope.
* The conditional cache flush predicating on RPC _not_ being in the
warm-up state has been replaced with unconditional flushing of the
database on object destruction (@UdjinM6, is this acceptable?)
## TODOs
This is a list of things that aren't within the scope of this pull
request but should be addressed in subsequent pull requests
* [ ] Definition of an interface that `CFlatDB` stores are expected to
implement
* [ ] Lock annotations for all potential uses of members protected by
the `cs` mutex in each manager object and store
* [ ] Additional comments documenting what each function and member does
* [ ] Deglobalization of affected managers
---------
Co-authored-by: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com>
2023-09-24 16:50:21 +02:00
|
|
|
/**
|
|
|
|
* ToString returns the string representation of the SporkManager.
|
|
|
|
*/
|
|
|
|
std::string ToString() const LOCKS_EXCLUDED(cs);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* CSporkManager is a higher-level class which manages the node's spork
|
|
|
|
* messages, rules for which sporks should be considered active/inactive, and
|
|
|
|
* processing for certain sporks (e.g. spork 12).
|
|
|
|
*/
|
|
|
|
class CSporkManager : public SporkStore
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
using db_type = CFlatDB<SporkStore>;
|
|
|
|
|
|
|
|
private:
|
|
|
|
const std::unique_ptr<db_type> m_db;
|
|
|
|
bool is_valid{false};
|
|
|
|
|
|
|
|
mutable Mutex cs_mapSporksCachedActive;
|
|
|
|
mutable std::unordered_map<const SporkId, bool> mapSporksCachedActive GUARDED_BY(cs_mapSporksCachedActive);
|
|
|
|
|
|
|
|
mutable Mutex cs_mapSporksCachedValues;
|
|
|
|
mutable std::unordered_map<SporkId, SporkValue> mapSporksCachedValues GUARDED_BY(cs_mapSporksCachedValues);
|
|
|
|
|
|
|
|
std::set<CKeyID> setSporkPubKeyIDs GUARDED_BY(cs);
|
|
|
|
int nMinSporkKeys GUARDED_BY(cs) {std::numeric_limits<int>::max()};
|
|
|
|
CKey sporkPrivKey GUARDED_BY(cs);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* SporkValueIfActive is used to get the value agreed upon by the majority
|
|
|
|
* of signed spork messages for a given Spork ID.
|
|
|
|
*/
|
|
|
|
std::optional<SporkValue> SporkValueIfActive(SporkId nSporkID) const EXCLUSIVE_LOCKS_REQUIRED(cs);
|
|
|
|
|
|
|
|
public:
|
|
|
|
CSporkManager();
|
|
|
|
~CSporkManager();
|
|
|
|
|
|
|
|
bool LoadCache();
|
|
|
|
|
|
|
|
bool IsValid() const { return is_valid; }
|
|
|
|
|
2018-10-02 11:02:27 +02:00
|
|
|
/**
|
|
|
|
* CheckAndRemove is defined to fulfill an interface as part of the on-disk
|
|
|
|
* cache used to cache sporks between runs. If sporks that are restored
|
|
|
|
* from cache do not have valid signatures when compared against the
|
|
|
|
* current spork private keys, they are removed from in-memory storage.
|
|
|
|
*
|
|
|
|
* This method was introduced along with the spork cache.
|
|
|
|
*/
|
2022-04-11 17:22:05 +02:00
|
|
|
void CheckAndRemove() LOCKS_EXCLUDED(cs);
|
2018-08-13 22:21:21 +02:00
|
|
|
|
2018-10-02 11:02:27 +02:00
|
|
|
/**
|
2023-01-19 16:32:16 +01:00
|
|
|
* ProcessMessage is used to call ProcessSpork and ProcessGetSporks. See below
|
2021-12-17 18:22:11 +01:00
|
|
|
*/
|
2023-12-18 13:15:06 +01:00
|
|
|
PeerMsgRet ProcessMessage(CNode& peer, CConnman& connman, std::string_view msg_type, CDataStream& vRecv);
|
2021-12-17 18:22:11 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* ProcessSpork is used to handle the 'spork' p2p message.
|
2018-10-02 11:02:27 +02:00
|
|
|
*
|
2021-12-17 18:22:11 +01:00
|
|
|
* For 'spork', it validates the spork and adds it to the internal spork storage and
|
2018-10-02 11:02:27 +02:00
|
|
|
* performs any necessary processing.
|
|
|
|
*/
|
2023-12-18 13:15:06 +01:00
|
|
|
PeerMsgRet ProcessSpork(const CNode& peer, CConnman& connman, CDataStream& vRecv) LOCKS_EXCLUDED(cs);
|
2021-12-17 18:22:11 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* ProcessGetSporks is used to handle the 'getsporks' p2p message.
|
|
|
|
*
|
|
|
|
* For 'getsporks', it sends active sporks to the requesting peer.
|
|
|
|
*/
|
2022-10-28 21:50:54 +02:00
|
|
|
void ProcessGetSporks(CNode& peer, CConnman& connman) LOCKS_EXCLUDED(cs);
|
2018-10-02 11:02:27 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* UpdateSpork is used by the spork RPC command to set a new spork value, sign
|
|
|
|
* and broadcast the spork message.
|
|
|
|
*/
|
2023-02-20 00:04:57 +01:00
|
|
|
bool UpdateSpork(SporkId nSporkID, SporkValue nValue, CConnman& connman) LOCKS_EXCLUDED(cs);
|
2015-02-09 21:54:51 +01:00
|
|
|
|
2018-10-02 11:02:27 +02:00
|
|
|
/**
|
|
|
|
* IsSporkActive returns a bool for time-based sporks, and should be used
|
|
|
|
* to determine whether the spork can be considered active or not.
|
|
|
|
* For value-based sporks such as SPORK_5_INSTANTSEND_MAX_VALUE, the spork
|
|
|
|
* value should not be considered a timestamp, but an integer value
|
|
|
|
* instead, and therefore this method doesn't make sense and should not be
|
|
|
|
* used.
|
|
|
|
*/
|
2021-01-22 06:57:42 +01:00
|
|
|
bool IsSporkActive(SporkId nSporkID) const;
|
2018-10-02 11:02:27 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* GetSporkValue returns the spork value given a Spork ID. If no active spork
|
|
|
|
* message has yet been received by the node, it returns the default value.
|
|
|
|
*/
|
2023-02-20 00:04:57 +01:00
|
|
|
SporkValue GetSporkValue(SporkId nSporkID) const LOCKS_EXCLUDED(cs);
|
2018-10-02 11:02:27 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* GetSporkIDByName returns the internal Spork ID given the spork name.
|
|
|
|
*/
|
2021-12-17 18:22:11 +01:00
|
|
|
static SporkId GetSporkIDByName(std::string_view strName);
|
2016-07-30 13:04:27 +02:00
|
|
|
|
2018-10-02 11:02:27 +02:00
|
|
|
/**
|
|
|
|
* GetSporkByHash returns a spork message given a hash of the spork message.
|
|
|
|
*
|
|
|
|
* This is used when a requesting peer sends a MSG_SPORK inventory message with
|
|
|
|
* the hash, to quickly lookup and return the full spork message. We maintain a
|
|
|
|
* hash-based index of sporks for this reason, and this function is the access
|
|
|
|
* point into that index.
|
|
|
|
*/
|
2022-10-19 20:37:28 +02:00
|
|
|
std::optional<CSporkMessage> GetSporkByHash(const uint256& hash) const LOCKS_EXCLUDED(cs);
|
2018-08-13 22:21:21 +02:00
|
|
|
|
2018-10-02 11:02:27 +02:00
|
|
|
/**
|
|
|
|
* SetSporkAddress is used to set a public key ID which will be used to
|
|
|
|
* verify spork signatures.
|
|
|
|
*
|
|
|
|
* This can be called multiple times to add multiple keys to the set of
|
|
|
|
* valid spork signers.
|
|
|
|
*/
|
2022-04-11 17:22:05 +02:00
|
|
|
bool SetSporkAddress(const std::string& strAddress) LOCKS_EXCLUDED(cs);
|
2018-10-02 11:02:27 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* SetMinSporkKeys is used to set the required spork signer threshold, for
|
|
|
|
* a spork to be considered active.
|
|
|
|
*
|
|
|
|
* This value must be at least a majority of the total number of spork
|
2021-04-15 19:58:04 +02:00
|
|
|
* keys, and for obvious reasons cannot be larger than that number.
|
2018-10-02 11:02:27 +02:00
|
|
|
*/
|
2022-04-11 17:22:05 +02:00
|
|
|
bool SetMinSporkKeys(int minSporkKeys) LOCKS_EXCLUDED(cs);
|
2018-10-02 11:02:27 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* SetPrivKey is used to set a spork key to enable setting / signing of
|
|
|
|
* spork values.
|
|
|
|
*
|
|
|
|
* This will return false if the private key does not match any spork
|
|
|
|
* address in the set of valid spork signers (see SetSporkAddress).
|
|
|
|
*/
|
2022-04-11 17:22:05 +02:00
|
|
|
bool SetPrivKey(const std::string& strPrivKey) LOCKS_EXCLUDED(cs);
|
2015-02-09 21:54:51 +01:00
|
|
|
};
|
|
|
|
|
2018-04-02 00:30:17 +02:00
|
|
|
#endif // BITCOIN_SPORK_H
|