Merge pull request #5427 from PastaPastaPasta/v19.2-rc.1-backports

backport: V19.2 rc.1 backports
This commit is contained in:
PastaPastaPasta 2023-06-13 09:36:29 -05:00 committed by GitHub
commit 44cd30f2f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 1050 additions and 289 deletions

View File

@ -1,9 +1,9 @@
AC_PREREQ([2.69])
define(_CLIENT_VERSION_MAJOR, 19)
define(_CLIENT_VERSION_MINOR, 1)
define(_CLIENT_VERSION_MINOR, 2)
define(_CLIENT_VERSION_BUILD, 0)
define(_CLIENT_VERSION_RC, 0)
define(_CLIENT_VERSION_IS_RELEASE, true)
define(_CLIENT_VERSION_RC, 1)
define(_CLIENT_VERSION_IS_RELEASE, false)
define(_COPYRIGHT_YEAR, 2023)
define(_COPYRIGHT_HOLDERS,[The %s developers])
define(_COPYRIGHT_HOLDERS_SUBSTITUTION,[[Dash Core]])
@ -713,6 +713,12 @@ case $host in
export PKG_CONFIG_PATH
fi
gmp_prefix=$($BREW --prefix gmp 2>/dev/null)
if test x$gmp_prefix != x; then
CPPFLAGS="$CPPFLAGS -I$gmp_prefix/include"
LDFLAGS="$LDFLAGS -L$gmp_prefix/lib"
fi
case $host in
*aarch64*)
dnl The preferred Homebrew prefix for Apple Silicon is /opt/homebrew.

View File

@ -1,6 +1,6 @@
PACKAGE=qt
$(package)_version=5.12.11
$(package)_download_path=https://download.qt.io/official_releases/qt/5.12/$($(package)_version)/submodules
$(package)_download_path=https://download.qt.io/archive/qt/5.12/$($(package)_version)/submodules
$(package)_suffix=everywhere-src-$($(package)_version).tar.xz
$(package)_file_name=qtbase-$($(package)_suffix)
$(package)_sha256_hash=1c1b4e33137ca77881074c140d54c3c9747e845a31338cfe8680f171f0bc3a39

View File

@ -0,0 +1,289 @@
# Dash Core version v19.0.0
Release is now available from:
<https://www.dash.org/downloads/#wallets>
This is a new major version release, bringing new features, various bugfixes
and other improvements.
This release is mandatory for all nodes.
Please report bugs using the issue tracker at GitHub:
<https://github.com/dashpay/dash/issues>
# Upgrading and downgrading
## How to Upgrade
If you are running an older version, shut it down. Wait until it has completely
shut down (which might take a few minutes for older versions), then run the
installer (on Windows) or just copy over /Applications/Dash-Qt (on Mac) or
dashd/dash-qt (on Linux). If you upgrade after DIP0003 activation and you were
using version < 0.13 you will have to reindex (start with -reindex-chainstate
or -reindex) to make sure your wallet has all the new data synced. Upgrading
from version 0.13 should not require any additional actions.
When upgrading from a version prior to 18.0.1, the
first startup of Dash Core will run a migration process which can take anywhere
from a few minutes to thirty minutes to finish. After the migration, a
downgrade to an older version is only possible with a reindex
(or reindex-chainstate).
## Downgrade warning
### Downgrade to a version < v19.0.0
Downgrading to a version older than v19.0.0 is not supported due to changes in the evodb database. If you need to use an older version, you must either reindex or re-sync the whole chain.
# Notable changes
## High-Performance Masternodes
In preparation for the release of Dash Platform to mainnet, a new masternode type has been added. High-performance masternodes will be responsible for hosting Dash Platform services (once they are on mainnet) in addition to the existing responsibilities like ChainLocks and InstantSend.
Activation of the DashCore v19.0 hard fork will enable registration of the new 4000 DASH collateral masternodes. Until Dash Platform is released to mainnet, high-performance masternodes will provide the same services as regular masternodes with one small exception. Regular masternodes will no longer participate in the Platform-specific LLMQ after the hard fork since they will not be responsible for hosting Dash Platform.
Note: In DashCore v19.0 the relative rewards and voting power are equivalent between regular and high-performance masternodes. Masternodes effectively receive one payout and one governance vote per 1000 DASH collateral. So, there is no difference in reward amount for running four regular masternodes or one high-performance masternode. In v19.0, high-performance masternodes simply receive payments in four consecutive blocks when they are selected for payout. Some frequently asked questions may be found at https://www.dash.org/hpmn-faq/.
## BLS Scheme Upgrade
Once the v19 hard fork is activated, all remaining messages containing BLS public keys or signatures will serialise them using the new basic BLS scheme.
The motivation behind this change is the need to be aligned with IETF standards.
List of affected messages:
`dsq`, `dstx`, `mnauth`, `govobj`, `govobjvote`, `qrinfo`, `qsigshare`, `qsigrec`, `isdlock`, `clsig`, and all DKG messages (`qfcommit`, `qcontrib`, `qcomplaint`, `qjustify`, `qpcommit`).
### `qfcommit`
Once the v19 hard fork is activated, `quorumPublicKey` will be serialised using the basic BLS scheme.
To support syncing of older blocks containing the transactions using the legacy BLS scheme, the `version` field indicates which scheme to use for serialisation of `quorumPublicKey`.
| Version | Version Description | Includes `quorumIndex` field |
|---------|--------------------------------------------------------|------------------------------|
| 1 | Non-rotated qfcommit serialised using legacy BLS scheme | No |
| 2 | Rotated qfcommit serialised using legacy BLS scheme | Yes |
| 3 | Non-rotated qfcommit serialised using basic BLS scheme | No |
| 4 | Rotated qfcommit serialised using basic BLS scheme | Yes |
### `MNLISTDIFF` P2P message
Starting with protocol version 70225, the following field is added to the `MNLISTDIFF` message between `cbTx` and `deletedQuorumsCount`.
| Field | Type | Size | Description |
|---------------------| ---- | ---- |-----------------------------------|
| version | uint16_t | 2 | Version of the `MNLISTDIFF` reply |
The `version` field indicates which BLS scheme is used to serialise the `pubKeyOperator` field for all SML entries of `mnList`.
| Version | Version Description |
|---------|-----------------------------------------------------------|
| 1 | Serialisation of `pubKeyOperator` using legacy BLS scheme |
| 2 | Serialisation of `pubKeyOperator` using basic BLS scheme |
### `ProTx` txs family
`proregtx` and `proupregtx` will support a new `version` value:
| Version | Version Description |
|---------|-----------------------------------------------------------|
| 1 | Serialisation of `pubKeyOperator` using legacy BLS scheme |
| 2 | Serialisation of `pubKeyOperator` using basic BLS scheme |
`proupservtx` and `prouprevtx` will support a new `version` value:
| Version | Version Description |
|---------|------------------------------------------------|
| 1 | Serialisation of `sig` using legacy BLS scheme |
| 2 | Serialisation of `sig` using basic BLS scheme |
### `MNHFTx`
`MNHFTx` will support a new `version` value:
| Version | Version Description |
|---------|------------------------------------------------|
| 1 | Serialisation of `sig` using legacy BLS scheme |
| 2 | Serialisation of `sig` using basic BLS scheme |
## Wallet
## Automatic wallet creation removed
Dash Core will no longer automatically create new wallets on startup. It will
load existing wallets specified by -wallet options on the command line or in
dash.conf or settings.json files. And by default it will also load a
top-level unnamed ("") wallet. However, if specified wallets don't exist,
Dash Core will now just log warnings instead of creating new wallets with
new keys and addresses like previous releases did.
New wallets can be created through the GUI (which has a more prominent create
wallet option), through the dash-wallet create command or the createwallet RPC.
## P2P and Network Changes
## Removal of reject network messages from Dash Core (BIP61)
The command line option to enable BIP61 (-enablebip61) has been removed.
Nodes on the network can not generally be trusted to send valid ("reject")
messages, so this should only ever be used when connected to a trusted node.
Please use the recommended alternatives if you rely on this deprecated feature:
- Testing or debugging of implementations of the Dash P2P network protocol
should be done by inspecting the log messages that are produced by a recent
version of Dash Core. Dash Core logs debug messages
(-debug=<category>) to a stream (-printtoconsole) or to a file
(-debuglogfile=<debug.log>).
- Testing the validity of a block can be achieved by specific RPCs:
- submitblock
- getblocktemplate with 'mode' set to 'proposal' for blocks with
- potentially invalid POW
- Testing the validity of a transaction can be achieved by specific RPCs:
- sendrawtransaction
- testmempoolaccept
## CoinJoin update
A minor update in several CoinJoin-related network messages improves support
for mixing from SPV clients. These changes make it easier for SPV clients to
participate in the CoinJoin process by using masternode information they can
readily obtain and verify via [DIP-0004](https://github.com/dashpay/dips/blob/master/dip-0004.md).
## Remote Procedure Call (RPC) Changes
### The new RPCs are:
- In order to support BLS public keys encoded in the legacy BLS scheme, `protx register_legacy`, `protx register_fund_legacy`, and `protx register_prepare_legacy` were added.
- `cleardiscouraged` clears all the already discouraged peers.
- The following RPCs were added: `protx register_hpmn`, `protx register_fund_hpmn`, `protx register_prepare_hpmn` and `protx update_service_hpmn`.
These HPMN RPCs correspond to the standard masternode RPCs but have the following additional mandatory arguments: `platformNodeID`, `platformP2PPort` and `platformHTTPPort`.
- `upgradewallet`
### The removed RPCs are:
None
### Changes in existing RPCs introduced through bitcoin backports:
- The `utxoupdatepsbt` RPC method has been updated to take a descriptors
argument. When provided, input and output scripts and keys will be filled in
when known. See the RPC help text for full details.
### Dash-specific changes in existing RPCs:
- `masternodelist`: New mode `recent` was added in order to hide banned masternodes for more than one `SuperblockCycle`. If the mode `recent` is used, then the reply mode is JSON (can be additionally filtered)
- `quorum info`: The new `previousConsecutiveDKGFailures` field will be returned for rotated LLMQs. This field will hold the number of previous consecutive DKG failures for the corresponding quorumIndex before the currently active one. Note: If no previous commitments were found then 0 will be returned for `previousConsecutiveDKGFailures`.
- `bls generate` and `bls fromsecret`: The new `scheme` field will be returned indicating which scheme was used to serialise the public key. Valid returned values are `legacy` and`basic`.
- `bls generate` and `bls fromsecret`: Both RPCs accept an incoming optional boolean argument `legacy` that enforces the use of legacy BLS scheme for the serialisation of the reply even if v19 is active.
- `masternode status`: now returns the type of the masternode.
- `masternode count`: now returns a detailed summary of total and enabled masternodes per type.
- `gobject getcurrentvotes`: reply is enriched by adding the vote weight at the end of each line. Possible values are 1 or 4. Example: "7cb20c883c6093b8489f795b3ec0aad0d9c2c2821610ae9ed938baaf42fec66d": "277e6345359071410ab691c21a3a16f8f46c9229c2f8ec8f028c9a95c0f1c0e7-1:1670019339:yes:funding:4"
- Once the v19 hard fork is activated, `protx register`, `protx register_fund`, and `protx register_prepare` RPCs will decode BLS operator public keys using the new basic BLS scheme.
Please check `help <command>` for more detailed information on specific RPCs.
## Command-line options
A number of command-line option changes were made related to testing and
removal of BIP61 support.
New cmd-line options:
- `llmqplatform` (devnet only)
- `unsafesqlitesync`
Removed cmd-line options:
- `enablebip61`
- `upgradewallet`
Changes in existing cmd-line options:
- `llmqinstantsend` and `llmqinstantsenddip0024` can be used in regtest now
- Passing an invalid `-rpcauth` argument now cause dashd to fail to start.
Please check `Help -> Command-line options` in Qt wallet or `dashd --help` for
more information.
## Backports from Bitcoin Core
This release introduces many updates from Bitcoin v0.18-v0.21 as well as numerous updates from Bitcoin v22 and more recent versions. Bitcoin changes that do not align with Dashs product needs, such as SegWit and RBF, are excluded from our backporting. For additional detail on whats included in Bitcoin, please refer to their release notes.
# v19.0.0 Change log
See detailed [set of changes](https://github.com/dashpay/dash/compare/v18.2.2...dashpay:v19.0.0).
# Credits
Thanks to everyone who directly contributed to this release:
- Kittywhiskers Van Gogh (kittywhiskers)
- Konstantin Akimov (knst)
- Odysseas Gabrielides (ogabrielides)
- Oleg Girko (OlegGirko)
- PastaPastaPasta
- thephez
- UdjinM6
- Vijay Das Manikpuri (vijaydasmp)
As well as everyone that submitted issues, reviewed pull requests, helped debug the release candidates, and write DIPs that were implemented in this release.
# Older releases
Dash was previously known as Darkcoin.
Darkcoin tree 0.8.x was a fork of Litecoin tree 0.8, original name was XCoin
which was first released on Jan/18/2014.
Darkcoin tree 0.9.x was the open source implementation of masternodes based on
the 0.8.x tree and was first released on Mar/13/2014.
Darkcoin tree 0.10.x used to be the closed source implementation of Darksend
which was released open source on Sep/25/2014.
Dash Core tree 0.11.x was a fork of Bitcoin Core tree 0.9,
Darkcoin was rebranded to Dash.
Dash Core tree 0.12.0.x was a fork of Bitcoin Core tree 0.10.
Dash Core tree 0.12.1.x was a fork of Bitcoin Core tree 0.12.
These release are considered obsolete. Old release notes can be found here:
- [v18.2.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.2.2.md) released Mar/21/2023
- [v18.2.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.2.1.md) released Jan/17/2023
- [v18.2.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.2.0.md) released Jan/01/2023
- [v18.1.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.1.1.md) released January/08/2023
- [v18.1.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.1.0.md) released October/09/2022
- [v18.0.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.0.2.md) released October/09/2022
- [v18.0.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.0.1.md) released August/17/2022
- [v0.17.0.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.17.0.3.md) released June/07/2021
- [v0.17.0.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.17.0.2.md) released May/19/2021
- [v0.16.1.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.16.1.1.md) released November/17/2020
- [v0.16.1.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.16.1.0.md) released November/14/2020
- [v0.16.0.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.16.0.1.md) released September/30/2020
- [v0.15.0.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.15.0.0.md) released Febrary/18/2020
- [v0.14.0.5](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.5.md) released December/08/2019
- [v0.14.0.4](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.4.md) released November/22/2019
- [v0.14.0.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.3.md) released August/15/2019
- [v0.14.0.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.2.md) released July/4/2019
- [v0.14.0.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.1.md) released May/31/2019
- [v0.14.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.md) released May/22/2019
- [v0.13.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.13.3.md) released Apr/04/2019
- [v0.13.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.13.2.md) released Mar/15/2019
- [v0.13.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.13.1.md) released Feb/9/2019
- [v0.13.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.13.0.md) released Jan/14/2019
- [v0.12.3.4](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.4.md) released Dec/14/2018
- [v0.12.3.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.3.md) released Sep/19/2018
- [v0.12.3.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.2.md) released Jul/09/2018
- [v0.12.3.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.1.md) released Jul/03/2018
- [v0.12.2.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.3.md) released Jan/12/2018
- [v0.12.2.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.2.md) released Dec/17/2017
- [v0.12.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.md) released Nov/08/2017
- [v0.12.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.1.md) released Feb/06/2017
- [v0.12.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.0.md) released Aug/15/2015
- [v0.11.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.2.md) released Mar/04/2015
- [v0.11.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.1.md) released Feb/10/2015
- [v0.11.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.0.md) released Jan/15/2015
- [v0.10.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.10.0.md) released Sep/25/2014
- [v0.9.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.9.0.md) released Mar/13/2014

View File

@ -147,11 +147,11 @@ bool CMnemonic::Check(SecureString mnemonic)
return fResult;
}
// passphrase must be at most 256 characters or code may crash
// passphrase must be at most 256 characters otherwise it would be truncated
void CMnemonic::ToSeed(SecureString mnemonic, SecureString passphrase, SecureVector& seedRet)
{
SecureString ssSalt = SecureString("mnemonic") + passphrase;
SecureVector vchSalt(ssSalt.begin(), ssSalt.end());
SecureVector vchSalt(ssSalt.begin(), ssSalt.begin() + strnlen(ssSalt.data(), 256));
seedRet.resize(64);
PKCS5_PBKDF2_HMAC_SHA512(mnemonic.c_str(), mnemonic.size(), vchSalt.data(), vchSalt.size(), 2048, 64, seedRet.data());
}

View File

@ -32,7 +32,7 @@ public:
static SecureString Generate(int strength); // strength in bits
static SecureString FromData(const SecureVector& data, int len);
static bool Check(SecureString mnemonic);
// passphrase must be at most 256 characters or code may crash
// passphrase must be at most 256 characters otherwise it would be truncated
static void ToSeed(SecureString mnemonic, SecureString passphrase, SecureVector& seedRet);
};

View File

@ -418,10 +418,7 @@ public:
CBLSLazyWrapper() :
vecBytes(BLSObject::SerSize, 0),
bufLegacyScheme(bls::bls_legacy_scheme.load())
{
// the all-zero buf is considered a valid buf, but the resulting object will return false for IsValid
bufValid = true;
}
{}
explicit CBLSLazyWrapper(const CBLSLazyWrapper& r)
{
@ -437,6 +434,7 @@ public:
if (r.bufValid) {
vecBytes = r.vecBytes;
} else {
vecBytes.resize(BLSObject::SerSize);
std::fill(vecBytes.begin(), vecBytes.end(), 0);
}
objInitialized = r.objInitialized;
@ -459,12 +457,9 @@ public:
{
std::unique_lock<std::mutex> l(mutex);
if (!objInitialized && !bufValid) {
// the all-zero buf is considered a valid buf
vecBytes.resize(BLSObject::SerSize);
std::fill(vecBytes.begin(), vecBytes.end(), 0);
bufLegacyScheme = specificLegacyScheme;
bufValid = true;
}
if (!bufValid || (bufLegacyScheme != specificLegacyScheme)) {
} else if (!bufValid || (bufLegacyScheme != specificLegacyScheme)) {
vecBytes = obj.ToByteVector(specificLegacyScheme);
bufValid = true;
bufLegacyScheme = specificLegacyScheme;
@ -476,7 +471,7 @@ public:
template<typename Stream>
inline void Serialize(Stream& s) const
{
Serialize(s, bls::bls_legacy_scheme.load());
Serialize(s, bufLegacyScheme);
}
template<typename Stream>
@ -493,13 +488,14 @@ public:
template<typename Stream>
inline void Unserialize(Stream& s) const
{
Unserialize(s, bls::bls_legacy_scheme.load());
Unserialize(s, bufLegacyScheme);
}
void Set(const BLSObject& _obj)
void Set(const BLSObject& _obj, const bool specificLegacyScheme)
{
std::unique_lock<std::mutex> l(mutex);
bufValid = false;
bufLegacyScheme = specificLegacyScheme;
objInitialized = true;
obj = _obj;
hash.SetNull();
@ -514,21 +510,14 @@ public:
if (!objInitialized) {
obj.SetByteVector(vecBytes, bufLegacyScheme);
if (!obj.IsValid()) {
// If setting of BLS object using one scheme failed, then we need to attempt again with the opposite scheme.
// This is due to the fact that LazyBLSWrapper receives a serialised buffer but attempts to create actual BLS object when needed.
// That could happen when the fork has been activated and the enforced scheme has switched.
obj.SetByteVector(vecBytes, !bufLegacyScheme);
if (obj.IsValid()) {
bufLegacyScheme = !bufLegacyScheme;
}
bufValid = false;
return invalidObj;
}
if (!obj.CheckMalleable(vecBytes, bufLegacyScheme)) {
bufValid = false;
objInitialized = false;
obj = invalidObj;
} else {
objInitialized = true;
return invalidObj;
}
objInitialized = true;
}
return obj;
}
@ -549,13 +538,16 @@ public:
return !(*this == r);
}
uint256 GetHash(const bool specificLegacyScheme = bls::bls_legacy_scheme.load()) const
uint256 GetHash() const
{
std::unique_lock<std::mutex> l(mutex);
if (!bufValid || bufLegacyScheme != specificLegacyScheme) {
vecBytes = obj.ToByteVector(specificLegacyScheme);
if (!objInitialized && !bufValid) {
vecBytes.resize(BLSObject::SerSize);
std::fill(vecBytes.begin(), vecBytes.end(), 0);
hash.SetNull();
} else if (!bufValid) {
vecBytes = obj.ToByteVector(bufLegacyScheme);
bufValid = true;
bufLegacyScheme = specificLegacyScheme;
hash.SetNull();
}
if (hash.IsNull()) {
@ -565,6 +557,21 @@ public:
}
return hash;
}
bool IsLegacy() const
{
return bufLegacyScheme;
}
void SetLegacy(bool specificLegacyScheme)
{
bufLegacyScheme = specificLegacyScheme;
}
std::string ToString() const
{
return Get().ToString(bufLegacyScheme);
}
};
using CBLSLazySignature = CBLSLazyWrapper<CBLSSignature>;
using CBLSLazyPublicKey = CBLSLazyWrapper<CBLSPublicKey>;

View File

@ -485,10 +485,10 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_V19].nFalloffCoeff = 5; // this corresponds to 10 periods
// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000000002d68cb6c090031f"); // 864000
consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000000002d68c6dc9ca04f3"); // 840000
// By default assume that the signatures in ancestors of this block are valid.
consensus.defaultAssumeValid = uint256S("0x0000005c35514190ef3c38d322f69412553dc7e1107ed5f92adc2935b90acc51"); // 864000
consensus.defaultAssumeValid = uint256S("0x000000cd7c3084499912ae893125c13e8c3c656abb6e511dcec6619c3d65a510"); // 840000
pchMessageStart[0] = 0xce;
pchMessageStart[1] = 0xe2;
@ -568,16 +568,16 @@ public:
{470000, uint256S("0x0000009303aeadf8cf3812f5c869691dbd4cb118ad20e9bf553be434bafe6a52")},
{794950, uint256S("0x000001860e4c7248a9c5cc3bc7106041750560dc5cd9b3a2641b49494bcff5f2")},
{808000, uint256S("0x00000104cb60a2b5e00a8a4259582756e5bf0dca201c0993c63f0e54971ea91a")},
{864000, uint256S("0x0000005c35514190ef3c38d322f69412553dc7e1107ed5f92adc2935b90acc51")},
{840000, uint256S("0x000000cd7c3084499912ae893125c13e8c3c656abb6e511dcec6619c3d65a510")},
}
};
// getchaintxstats 17280 0000005c35514190ef3c38d322f69412553dc7e1107ed5f92adc2935b90acc51
// getchaintxstats 17280 000000cd7c3084499912ae893125c13e8c3c656abb6e511dcec6619c3d65a510
chainTxData = ChainTxData{
1680868209, // * UNIX timestamp of last known number of transactions (Block 771537)
5847013, // * total number of transactions between genesis and that timestamp
1676885923, // * UNIX timestamp of last known number of transactions (Block 840000)
5776047, // * total number of transactions between genesis and that timestamp
// (the tx=... number in the ChainStateFlushed debug.log lines)
0.01994632331955769, // * estimated number of transactions per second after that timestamp
0.01120953982471268, // * estimated number of transactions per second after that timestamp
};
}
};

View File

@ -41,22 +41,22 @@ bool CCoinJoinEntry::AddScriptSig(const CTxIn& txin)
return false;
}
uint256 CCoinJoinQueue::GetSignatureHash() const
uint256 CCoinJoinQueue::GetSignatureHash(bool legacy) const
{
return SerializeHash(*this);
int version = legacy ? COINJOIN_PROTX_HASH_PROTO_VERSION - 1 : PROTOCOL_VERSION;
return SerializeHash(*this, SER_GETHASH, version);
}
bool CCoinJoinQueue::Sign()
{
if (!fMasternodeMode) return false;
uint256 hash = GetSignatureHash();
bool legacy_bls_scheme = !llmq::utils::IsV19Active(::ChainActive().Tip());
uint256 hash = GetSignatureHash(legacy_bls_scheme);
CBLSSignature sig = WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.blsKeyOperator->Sign(hash));
if (!sig.IsValid()) {
return false;
}
bool legacy_bls_scheme = !llmq::utils::IsV19Active(::ChainActive().Tip());
vchSig = sig.ToByteVector(legacy_bls_scheme);
return true;
@ -64,7 +64,8 @@ bool CCoinJoinQueue::Sign()
bool CCoinJoinQueue::CheckSignature(const CBLSPublicKey& blsPubKey) const
{
if (!CBLSSignature(vchSig).VerifyInsecure(blsPubKey, GetSignatureHash())) {
bool legacy_bls_scheme = !llmq::utils::IsV19Active(::ChainActive().Tip());
if (!CBLSSignature(vchSig).VerifyInsecure(blsPubKey, GetSignatureHash(legacy_bls_scheme))) {
LogPrint(BCLog::COINJOIN, "CCoinJoinQueue::CheckSignature -- VerifyInsecure() failed\n");
return false;
}
@ -89,21 +90,22 @@ bool CCoinJoinQueue::IsTimeOutOfBounds(int64_t current_time) const
nTime - current_time > COINJOIN_QUEUE_TIMEOUT;
}
uint256 CCoinJoinBroadcastTx::GetSignatureHash() const
uint256 CCoinJoinBroadcastTx::GetSignatureHash(bool legacy) const
{
return SerializeHash(*this);
int version = legacy ? COINJOIN_PROTX_HASH_PROTO_VERSION - 1 : PROTOCOL_VERSION;
return SerializeHash(*this, SER_GETHASH, version);
}
bool CCoinJoinBroadcastTx::Sign()
{
if (!fMasternodeMode) return false;
uint256 hash = GetSignatureHash();
bool legacy_bls_scheme = !llmq::utils::IsV19Active(::ChainActive().Tip());
uint256 hash = GetSignatureHash(legacy_bls_scheme);
CBLSSignature sig = WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.blsKeyOperator->Sign(hash));
if (!sig.IsValid()) {
return false;
}
bool legacy_bls_scheme = !llmq::utils::IsV19Active(::ChainActive().Tip());
vchSig = sig.ToByteVector(legacy_bls_scheme);
return true;
@ -111,7 +113,8 @@ bool CCoinJoinBroadcastTx::Sign()
bool CCoinJoinBroadcastTx::CheckSignature(const CBLSPublicKey& blsPubKey) const
{
if (!CBLSSignature(vchSig).VerifyInsecure(blsPubKey, GetSignatureHash())) {
bool legacy_bls_scheme = !llmq::utils::IsV19Active(::ChainActive().Tip());
if (!CBLSSignature(vchSig).VerifyInsecure(blsPubKey, GetSignatureHash(legacy_bls_scheme))) {
LogPrint(BCLog::COINJOIN, "CCoinJoinBroadcastTx::CheckSignature -- VerifyInsecure() failed\n");
return false;
}

View File

@ -219,7 +219,7 @@ public:
{
READWRITE(obj.nDenom);
if (s.GetVersion() < COINJOIN_PROTX_HASH_PROTO_VERSION || (s.GetType() & SER_GETHASH)) {
if (s.GetVersion() < COINJOIN_PROTX_HASH_PROTO_VERSION) {
READWRITE(obj.masternodeOutpoint);
} else {
READWRITE(obj.m_protxHash);
@ -230,7 +230,7 @@ public:
}
}
[[nodiscard]] uint256 GetSignatureHash() const;
[[nodiscard]] uint256 GetSignatureHash(bool legacy) const;
/** Sign this mixing transaction
* return true if all conditions are met:
* 1) we have an active Masternode,
@ -292,7 +292,7 @@ public:
{
READWRITE(obj.tx);
if (s.GetVersion() < COINJOIN_PROTX_HASH_PROTO_VERSION || (s.GetType() & SER_GETHASH)) {
if (s.GetVersion() < COINJOIN_PROTX_HASH_PROTO_VERSION) {
READWRITE(obj.masternodeOutpoint);
} else {
READWRITE(obj.m_protxHash);
@ -317,7 +317,7 @@ public:
return *this != CCoinJoinBroadcastTx();
}
[[nodiscard]] uint256 GetSignatureHash() const;
[[nodiscard]] uint256 GetSignatureHash(bool legacy) const;
bool Sign();
[[nodiscard]] bool CheckSignature(const CBLSPublicKey& blsPubKey) const;

View File

@ -121,8 +121,7 @@ bool CalcCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindexPrev
int64_t nTime2 = GetTimeMicros(); nTimeDMN += nTime2 - nTime1;
LogPrint(BCLog::BENCHMARK, " - BuildNewListFromBlock: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1), nTimeDMN * 0.000001);
bool v19active = llmq::utils::IsV19Active(pindexPrev);
CSimplifiedMNList sml(tmpMNList, v19active);
CSimplifiedMNList sml(tmpMNList);
int64_t nTime3 = GetTimeMicros(); nTimeSMNL += nTime3 - nTime2;
LogPrint(BCLog::BENCHMARK, " - CSimplifiedMNList: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeSMNL * 0.000001);

View File

@ -25,8 +25,8 @@
#include <memory>
static const std::string DB_LIST_SNAPSHOT = "dmn_S2";
static const std::string DB_LIST_DIFF = "dmn_D2";
static const std::string DB_LIST_SNAPSHOT = "dmn_S3";
static const std::string DB_LIST_DIFF = "dmn_D3";
std::unique_ptr<CDeterministicMNManager> deterministicMNManager;
@ -405,23 +405,18 @@ CDeterministicMNListDiff CDeterministicMNList::BuildDiff(const CDeterministicMNL
CSimplifiedMNListDiff CDeterministicMNList::BuildSimplifiedDiff(const CDeterministicMNList& to, bool extended) const
{
bool v19active = llmq::utils::IsV19Active(::ChainActive().Tip());
CSimplifiedMNListDiff diffRet;
diffRet.baseBlockHash = blockHash;
diffRet.blockHash = to.blockHash;
diffRet.nVersion = v19active ? CSimplifiedMNListDiff::BASIC_BLS_VERSION : CSimplifiedMNListDiff::LEGACY_BLS_VERSION;
to.ForEachMN(false, [&](const auto& toPtr) {
auto fromPtr = GetMN(toPtr.proTxHash);
if (fromPtr == nullptr) {
CSimplifiedMNListEntry sme(toPtr);
sme.nVersion = diffRet.nVersion;
diffRet.mnList.push_back(std::move(sme));
} else {
CSimplifiedMNListEntry sme1(toPtr);
CSimplifiedMNListEntry sme2(*fromPtr);
sme1.nVersion = diffRet.nVersion;
sme2.nVersion = diffRet.nVersion;
if ((sme1 != sme2) ||
(extended && (sme1.scriptPayout != sme2.scriptPayout || sme1.scriptOperatorPayout != sme2.scriptOperatorPayout))) {
diffRet.mnList.push_back(std::move(sme1));
@ -463,48 +458,6 @@ CDeterministicMNList CDeterministicMNList::ApplyDiff(const CBlockIndex* pindex,
return result;
}
// RepopulateUniquePropertyMap clears internal mnUniquePropertyMap, and repopulate it with currently MNs unique properties.
// This is needed when the v19 fork activates, we need to store again pubKeyOperator in the mnUniquePropertyMap.
// pubKeyOperator don't differ between the two schemes (legacy and basic(v19)) but their serialisation do: hence their hash.
// And because mnUniquePropertyMap store only hashes, then we need to re-calculate hashes and repopulate.
void CDeterministicMNList::RepopulateUniquePropertyMap() {
decltype(mnUniquePropertyMap) mnUniquePropertyMapEmpty;
mnUniquePropertyMap = mnUniquePropertyMapEmpty;
for (const auto &p: mnMap) {
auto dmn = p.second;
if (!AddUniqueProperty(*dmn, dmn->collateralOutpoint)) {
throw (std::runtime_error(
strprintf("%s: Can't add a masternode %s with a duplicate collateralOutpoint=%s", __func__,
dmn->proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort())));
}
if (dmn->pdmnState->addr != CService() && !AddUniqueProperty(*dmn, dmn->pdmnState->addr)) {
throw (std::runtime_error(strprintf("%s: Can't add a masternode %s with a duplicate address=%s", __func__,
dmn->proTxHash.ToString(),
dmn->pdmnState->addr.ToStringIPPort(false))));
}
if (!AddUniqueProperty(*dmn, dmn->pdmnState->keyIDOwner)) {
throw (std::runtime_error(
strprintf("%s: Can't add a masternode %s with a duplicate keyIDOwner=%s", __func__,
dmn->proTxHash.ToString(), EncodeDestination(PKHash(dmn->pdmnState->keyIDOwner)))));
}
if (dmn->pdmnState->pubKeyOperator.Get().IsValid() &&
!AddUniqueProperty(*dmn, dmn->pdmnState->pubKeyOperator)) {
throw (std::runtime_error(
strprintf("%s: Can't add a masternode %s with a duplicate pubKeyOperator=%s", __func__,
dmn->proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.Get().ToString())));
}
if (dmn->nType == MnType::HighPerformance) {
if (!AddUniqueProperty(*dmn, dmn->pdmnState->platformNodeID)) {
throw (std::runtime_error(
strprintf("%s: Can't add a masternode %s with a duplicate platformNodeID=%s", __func__,
dmn->proTxHash.ToString(), dmn->pdmnState->platformNodeID.ToString())));
}
}
}
}
void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTotalCount)
{
assert(dmn != nullptr);
@ -538,7 +491,7 @@ void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTota
if (dmn->pdmnState->pubKeyOperator.Get().IsValid() && !AddUniqueProperty(*dmn, dmn->pdmnState->pubKeyOperator)) {
mnUniquePropertyMap = mnUniquePropertyMapSaved;
throw(std::runtime_error(strprintf("%s: Can't add a masternode %s with a duplicate pubKeyOperator=%s", __func__,
dmn->proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.Get().ToString())));
dmn->proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.ToString())));
}
if (dmn->nType == MnType::HighPerformance) {
@ -561,7 +514,6 @@ void CDeterministicMNList::UpdateMN(const CDeterministicMN& oldDmn, const std::s
{
auto dmn = std::make_shared<CDeterministicMN>(oldDmn);
auto oldState = dmn->pdmnState;
dmn->pdmnState = pdmnState;
// All mnUniquePropertyMap's updates must be atomic.
// Using this temporary map as a checkpoint to roll back to in case of any issues.
@ -580,16 +532,17 @@ void CDeterministicMNList::UpdateMN(const CDeterministicMN& oldDmn, const std::s
if (!UpdateUniqueProperty(*dmn, oldState->pubKeyOperator, pdmnState->pubKeyOperator)) {
mnUniquePropertyMap = mnUniquePropertyMapSaved;
throw(std::runtime_error(strprintf("%s: Can't update a masternode %s with a duplicate pubKeyOperator=%s", __func__,
oldDmn.proTxHash.ToString(), pdmnState->pubKeyOperator.Get().ToString())));
oldDmn.proTxHash.ToString(), pdmnState->pubKeyOperator.ToString())));
}
if (dmn->nType == MnType::HighPerformance) {
if (!UpdateUniqueProperty(*dmn, oldState->platformNodeID, dmn->pdmnState->platformNodeID)) {
if (!UpdateUniqueProperty(*dmn, oldState->platformNodeID, pdmnState->platformNodeID)) {
mnUniquePropertyMap = mnUniquePropertyMapSaved;
throw(std::runtime_error(strprintf("%s: Can't update a masternode %s with a duplicate platformNodeID=%s", __func__,
dmn->proTxHash.ToString(), dmn->pdmnState->platformNodeID.ToString())));
oldDmn.proTxHash.ToString(), pdmnState->platformNodeID.ToString())));
}
}
dmn->pdmnState = pdmnState;
mnMap = mnMap.set(oldDmn.proTxHash, dmn);
}
@ -639,7 +592,7 @@ void CDeterministicMNList::RemoveMN(const uint256& proTxHash)
if (dmn->pdmnState->pubKeyOperator.Get().IsValid() && !DeleteUniqueProperty(*dmn, dmn->pdmnState->pubKeyOperator)) {
mnUniquePropertyMap = mnUniquePropertyMapSaved;
throw(std::runtime_error(strprintf("%s: Can't delete a masternode %s with a pubKeyOperator=%s", __func__,
proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.Get().ToString())));
proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.ToString())));
}
if (dmn->nType == MnType::HighPerformance) {
@ -687,19 +640,11 @@ bool CDeterministicMNManager::ProcessBlock(const CBlock& block, const CBlockInde
newList.SetBlockHash(block.GetHash());
// If the fork is active for pindex block, then we need to repopulate property map
// (Check documentation of CDeterministicMNList::RepopulateUniquePropertyMap()).
// This is needed only when base list is pre-v19 fork and pindex is post-v19 fork.
bool v19_just_activated = pindex == llmq::utils::V19ActivationIndex(pindex);
if (v19_just_activated) {
newList.RepopulateUniquePropertyMap();
}
oldList = GetListForBlock(pindex->pprev);
diff = oldList.BuildDiff(newList);
m_evoDb.Write(std::make_pair(DB_LIST_DIFF, newList.GetBlockHash()), diff);
if ((nHeight % DISK_SNAPSHOT_PERIOD) == 0 || oldList.GetHeight() == -1 || v19_just_activated) {
if ((nHeight % DISK_SNAPSHOT_PERIOD) == 0 || oldList.GetHeight() == -1) {
m_evoDb.Write(std::make_pair(DB_LIST_SNAPSHOT, newList.GetBlockHash()), newList);
mnListsCache.emplace(newList.GetBlockHash(), newList);
LogPrintf("CDeterministicMNManager::%s -- Wrote snapshot. nHeight=%d, mapCurMNs.allMNsCount=%d\n",
@ -940,12 +885,14 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C
return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-hash");
}
auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
if (newState->pubKeyOperator.Get() != proTx.pubKeyOperator) {
if (newState->pubKeyOperator != proTx.pubKeyOperator) {
// reset all operator related fields and put MN into PoSe-banned state in case the operator key changes
newState->ResetOperatorFields();
newState->BanIfNotBanned(nHeight);
// we update pubKeyOperator here, make sure state version matches
newState->nVersion = proTx.nVersion;
newState->pubKeyOperator = proTx.pubKeyOperator;
}
newState->pubKeyOperator.Set(proTx.pubKeyOperator);
newState->keyIDVoting = proTx.keyIDVoting;
newState->scriptPayout = proTx.scriptPayout;
@ -1228,9 +1175,14 @@ void CDeterministicMNManager::CleanupCache(int nHeight)
std::vector<uint256> toDeleteDiffs;
for (const auto& p : mnListsCache) {
if (p.second.GetHeight() + LIST_DIFFS_CACHE_SIZE < nHeight) {
// too old, drop it
toDeleteLists.emplace_back(p.first);
continue;
}
if (tipIndex != nullptr && p.first == tipIndex->GetBlockHash()) {
// it's a snapshot for the tip, keep it
continue;
}
bool fQuorumCache = ranges::any_of(Params().GetConsensus().llmqs, [&nHeight, &p](const auto& params){
return (p.second.GetHeight() % params.dkgInterval == 0) &&
(p.second.GetHeight() + params.dkgInterval * (params.keepOldConnections + 1) >= nHeight);
@ -1239,13 +1191,8 @@ void CDeterministicMNManager::CleanupCache(int nHeight)
// at least one quorum could be using it, keep it
continue;
}
// no alive quorums using it, see if it was a cache for the tip or for a now outdated quorum
if (tipIndex && tipIndex->pprev && (p.first == tipIndex->pprev->GetBlockHash())) {
toDeleteLists.emplace_back(p.first);
} else if (ranges::any_of(Params().GetConsensus().llmqs,
[&p](const auto& llmqParams){ return p.second.GetHeight() % llmqParams.dkgInterval == 0; })) {
toDeleteLists.emplace_back(p.first);
}
// none of the above, drop it
toDeleteLists.emplace_back(p.first);
}
for (const auto& h : toDeleteLists) {
mnListsCache.erase(h);
@ -1266,6 +1213,7 @@ bool CDeterministicMNManager::MigrateDBIfNeeded()
static const std::string DB_OLD_LIST_SNAPSHOT = "dmn_S";
static const std::string DB_OLD_LIST_DIFF = "dmn_D";
static const std::string DB_OLD_BEST_BLOCK = "b_b2";
static const std::string DB_OLD_BEST_BLOCK2 = "b_b3";
LOCK(cs_main);
@ -1277,11 +1225,17 @@ bool CDeterministicMNManager::MigrateDBIfNeeded()
return m_evoDb.IsEmpty();
}
if (m_evoDb.GetRawDB().Exists(EVODB_BEST_BLOCK)) {
if (m_evoDb.GetRawDB().Exists(EVODB_BEST_BLOCK) || m_evoDb.GetRawDB().Exists(DB_OLD_BEST_BLOCK2)) {
LogPrintf("CDeterministicMNManager::%s -- migration already done. skipping.\n", __func__);
return true;
}
if (::ChainActive().Tip()->pprev != nullptr && llmq::utils::IsV19Active(::ChainActive().Tip()->pprev)) {
// too late
LogPrintf("CDeterministicMNManager::%s -- migration is not possible\n", __func__);
return false;
}
// Removing the old EVODB_BEST_BLOCK value early results in older version to crash immediately, even if the upgrade
// process is cancelled in-between. But if the new version sees that the old EVODB_BEST_BLOCK is already removed,
// then we must assume that the upgrade process was already running before but was interrupted.
@ -1304,15 +1258,15 @@ bool CDeterministicMNManager::MigrateDBIfNeeded()
for (const auto nHeight : irange::range(Params().GetConsensus().DIP0003Height, ::ChainActive().Height() + 1)) {
auto pindex = ::ChainActive()[nHeight];
// Unserialise CDeterministicMNListDiff using CURRENT_MN_FORMAT and set it's type to the default value TYPE_REGULAR_MASTERNODE
// It will be later written with format MN_TYPE_FORMAT which includes the type field.
// Unserialise CDeterministicMNListDiff using MN_OLD_FORMAT and set it's type to the default value TYPE_REGULAR_MASTERNODE
// It will be later written with format MN_CURRENT_FORMAT which includes the type field and MN state bls version.
CDataStream diff_data(SER_DISK, CLIENT_VERSION);
if (!m_evoDb.GetRawDB().ReadDataStream(std::make_pair(DB_OLD_LIST_DIFF, pindex->GetBlockHash()), diff_data)) {
LogPrintf("CDeterministicMNManager::%s -- missing CDeterministicMNListDiff at height %d\n", __func__, nHeight);
return false;
}
CDeterministicMNListDiff mndiff;
mndiff.Unserialize(diff_data, CDeterministicMN::CURRENT_MN_FORMAT);
mndiff.Unserialize(diff_data, CDeterministicMN::MN_OLD_FORMAT);
batch.Write(std::make_pair(DB_LIST_DIFF, pindex->GetBlockHash()), mndiff);
CDataStream snapshot_data(SER_DISK, CLIENT_VERSION);
if (!m_evoDb.GetRawDB().ReadDataStream(std::make_pair(DB_OLD_LIST_SNAPSHOT, pindex->GetBlockHash()), snapshot_data)) {
@ -1320,7 +1274,7 @@ bool CDeterministicMNManager::MigrateDBIfNeeded()
continue;
}
CDeterministicMNList mnList;
mnList.Unserialize(snapshot_data, CDeterministicMN::CURRENT_MN_FORMAT);
mnList.Unserialize(snapshot_data, CDeterministicMN::MN_OLD_FORMAT);
batch.Write(std::make_pair(DB_LIST_SNAPSHOT, pindex->GetBlockHash()), mnList);
m_evoDb.GetRawDB().WriteBatch(batch);
batch.Clear();
@ -1329,7 +1283,7 @@ bool CDeterministicMNManager::MigrateDBIfNeeded()
m_evoDb.GetRawDB().WriteBatch(batch);
// Writing EVODB_BEST_BLOCK (which is b_b3 now) marks the DB as upgraded
// Writing EVODB_BEST_BLOCK (which is b_b4 now) marks the DB as upgraded
auto dbTx = m_evoDb.BeginTransaction();
m_evoDb.WriteBestBlock(::ChainActive().Tip()->GetBlockHash());
dbTx->Commit();
@ -1345,6 +1299,111 @@ bool CDeterministicMNManager::MigrateDBIfNeeded()
LogPrintf("CDeterministicMNManager::%s -- done compacting database\n", __func__);
// flush it to disk
if (!m_evoDb.CommitRootTransaction()) {
LogPrintf("CDeterministicMNManager::%s -- failed to commit to evoDB\n", __func__);
return false;
}
return true;
}
bool CDeterministicMNManager::MigrateDBIfNeeded2()
{
static const std::string DB_OLD_LIST_SNAPSHOT = "dmn_S2";
static const std::string DB_OLD_LIST_DIFF = "dmn_D2";
static const std::string DB_OLD_BEST_BLOCK = "b_b3";
LOCK(cs_main);
LogPrintf("CDeterministicMNManager::%s -- upgrading DB to migrate MN state bls version\n", __func__);
if (::ChainActive().Tip() == nullptr) {
// should have no records
LogPrintf("CDeterministicMNManager::%s -- Chain empty. evoDB:%d.\n", __func__, m_evoDb.IsEmpty());
return m_evoDb.IsEmpty();
}
if (m_evoDb.GetRawDB().Exists(EVODB_BEST_BLOCK)) {
LogPrintf("CDeterministicMNManager::%s -- migration already done. skipping.\n", __func__);
return true;
}
if (::ChainActive().Tip()->pprev != nullptr && llmq::utils::IsV19Active(::ChainActive().Tip()->pprev)) {
// too late
LogPrintf("CDeterministicMNManager::%s -- migration is not possible\n", __func__);
return false;
}
// Removing the old EVODB_BEST_BLOCK value early results in older version to crash immediately, even if the upgrade
// process is cancelled in-between. But if the new version sees that the old EVODB_BEST_BLOCK is already removed,
// then we must assume that the upgrade process was already running before but was interrupted.
if (::ChainActive().Height() > 1 && !m_evoDb.GetRawDB().Exists(DB_OLD_BEST_BLOCK)) {
LogPrintf("CDeterministicMNManager::%s -- previous migration attempt failed.\n", __func__);
return false;
}
m_evoDb.GetRawDB().Erase(DB_OLD_BEST_BLOCK);
if (::ChainActive().Height() < Params().GetConsensus().DIP0003Height) {
// not reached DIP3 height yet, so no upgrade needed
LogPrintf("CDeterministicMNManager::%s -- migration not needed. dip3 not reached\n", __func__);
auto dbTx = m_evoDb.BeginTransaction();
m_evoDb.WriteBestBlock(::ChainActive().Tip()->GetBlockHash());
dbTx->Commit();
return true;
}
CDBBatch batch(m_evoDb.GetRawDB());
for (const auto nHeight : irange::range(Params().GetConsensus().DIP0003Height, ::ChainActive().Height() + 1)) {
auto pindex = ::ChainActive()[nHeight];
// Unserialise CDeterministicMNListDiff using MN_TYPE_FORMAT and set MN state bls version to LEGACY_BLS_VERSION.
// It will be later written with format MN_CURRENT_FORMAT which includes the type field.
CDataStream diff_data(SER_DISK, CLIENT_VERSION);
if (!m_evoDb.GetRawDB().ReadDataStream(std::make_pair(DB_OLD_LIST_DIFF, pindex->GetBlockHash()), diff_data)) {
LogPrintf("CDeterministicMNManager::%s -- missing CDeterministicMNListDiff at height %d\n", __func__, nHeight);
return false;
}
CDeterministicMNListDiff mndiff;
mndiff.Unserialize(diff_data, CDeterministicMN::MN_TYPE_FORMAT);
batch.Write(std::make_pair(DB_LIST_DIFF, pindex->GetBlockHash()), mndiff);
CDataStream snapshot_data(SER_DISK, CLIENT_VERSION);
if (!m_evoDb.GetRawDB().ReadDataStream(std::make_pair(DB_OLD_LIST_SNAPSHOT, pindex->GetBlockHash()), snapshot_data)) {
// it's ok, we write snapshots every DISK_SNAPSHOT_PERIOD blocks only
continue;
}
CDeterministicMNList mnList;
mnList.Unserialize(snapshot_data, CDeterministicMN::MN_TYPE_FORMAT);
batch.Write(std::make_pair(DB_LIST_SNAPSHOT, pindex->GetBlockHash()), mnList);
m_evoDb.GetRawDB().WriteBatch(batch);
batch.Clear();
LogPrintf("CDeterministicMNManager::%s -- wrote snapshot at height %d\n", __func__, nHeight);
}
m_evoDb.GetRawDB().WriteBatch(batch);
// Writing EVODB_BEST_BLOCK (which is b_b4 now) marks the DB as upgraded
auto dbTx = m_evoDb.BeginTransaction();
m_evoDb.WriteBestBlock(::ChainActive().Tip()->GetBlockHash());
dbTx->Commit();
LogPrintf("CDeterministicMNManager::%s -- done migrating\n", __func__);
m_evoDb.GetRawDB().Erase(DB_OLD_LIST_DIFF);
m_evoDb.GetRawDB().Erase(DB_OLD_LIST_SNAPSHOT);
LogPrintf("CDeterministicMNManager::%s -- done cleaning old data\n", __func__);
m_evoDb.GetRawDB().CompactFull();
LogPrintf("CDeterministicMNManager::%s -- done compacting database\n", __func__);
// flush it to disk
if (!m_evoDb.CommitRootTransaction()) {
LogPrintf("CDeterministicMNManager::%s -- failed to commit to evoDB\n", __func__);
return false;
}
return true;
}

View File

@ -43,8 +43,10 @@ private:
uint64_t internalId{std::numeric_limits<uint64_t>::max()};
public:
static constexpr uint16_t CURRENT_MN_FORMAT = 0;
static constexpr uint16_t MN_OLD_FORMAT = 0;
static constexpr uint16_t MN_TYPE_FORMAT = 1;
static constexpr uint16_t MN_VERSION_FORMAT = 2;
static constexpr uint16_t MN_CURRENT_FORMAT = MN_VERSION_FORMAT;
CDeterministicMN() = delete; // no default constructor, must specify internalId
explicit CDeterministicMN(uint64_t _internalId, MnType mnType = MnType::Regular) :
@ -74,12 +76,16 @@ public:
READWRITE(VARINT(internalId));
READWRITE(collateralOutpoint);
READWRITE(nOperatorReward);
// We need to read CDeterministicMNState using the old format only when called with CURRENT_MN_FORMAT on Unserialize()
// We need to read CDeterministicMNState using the old format only when called with MN_OLD_FORMAT or MN_TYPE_FORMAT on Unserialize()
// Serialisation (writing) will be done always using new format
if (ser_action.ForRead() && format_version == CURRENT_MN_FORMAT) {
if (ser_action.ForRead() && format_version == MN_OLD_FORMAT) {
CDeterministicMNState_Oldformat old_state;
READWRITE(old_state);
pdmnState = std::make_shared<const CDeterministicMNState>(old_state);
} else if (ser_action.ForRead() && format_version == MN_TYPE_FORMAT) {
CDeterministicMNState_mntype_format old_state;
READWRITE(old_state);
pdmnState = std::make_shared<const CDeterministicMNState>(old_state);
} else {
READWRITE(pdmnState);
}
@ -98,11 +104,11 @@ public:
template<typename Stream>
void Serialize(Stream& s) const
{
const_cast<CDeterministicMN*>(this)->SerializationOp(s, CSerActionSerialize(), MN_TYPE_FORMAT);
const_cast<CDeterministicMN*>(this)->SerializationOp(s, CSerActionSerialize(), MN_CURRENT_FORMAT);
}
template <typename Stream>
void Unserialize(Stream& s, const uint8_t format_version = MN_TYPE_FORMAT)
void Unserialize(Stream& s, const uint8_t format_version = MN_CURRENT_FORMAT)
{
SerializationOp(s, CSerActionUnserialize(), format_version);
}
@ -204,14 +210,14 @@ public:
}
template<typename Stream>
void Unserialize(Stream& s, const uint8_t format_version = CDeterministicMN::MN_TYPE_FORMAT) {
void Unserialize(Stream& s, const uint8_t format_version = CDeterministicMN::MN_CURRENT_FORMAT) {
mnMap = MnMap();
mnUniquePropertyMap = MnUniquePropertyMap();
mnInternalIdMap = MnInternalIdMap();
SerializationOpBase(s, CSerActionUnserialize());
bool evodb_migration = (format_version == CDeterministicMN::CURRENT_MN_FORMAT);
bool evodb_migration = (format_version == CDeterministicMN::MN_OLD_FORMAT || format_version == CDeterministicMN::MN_TYPE_FORMAT);
size_t cnt = ReadCompactSize(s);
for (size_t i = 0; i < cnt; i++) {
if (evodb_migration) {
@ -385,8 +391,6 @@ public:
[[nodiscard]] CSimplifiedMNListDiff BuildSimplifiedDiff(const CDeterministicMNList& to, bool extended) const;
[[nodiscard]] CDeterministicMNList ApplyDiff(const CBlockIndex* pindex, const CDeterministicMNListDiff& diff) const;
void RepopulateUniquePropertyMap();
void AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTotalCount = true);
void UpdateMN(const CDeterministicMN& oldDmn, const std::shared_ptr<const CDeterministicMNState>& pdmnState);
void UpdateMN(const uint256& proTxHash, const std::shared_ptr<const CDeterministicMNState>& pdmnState);
@ -396,12 +400,12 @@ public:
template <typename T>
[[nodiscard]] bool HasUniqueProperty(const T& v) const
{
return mnUniquePropertyMap.count(::SerializeHash(v)) != 0;
return mnUniquePropertyMap.count(GetUniquePropertyHash(v)) != 0;
}
template <typename T>
[[nodiscard]] CDeterministicMNCPtr GetUniquePropertyMN(const T& v) const
{
auto p = mnUniquePropertyMap.find(::SerializeHash(v));
auto p = mnUniquePropertyMap.find(GetUniquePropertyHash(v));
if (!p) {
return nullptr;
}
@ -409,6 +413,14 @@ public:
}
private:
template <typename T>
[[nodiscard]] uint256 GetUniquePropertyHash(const T& v) const
{
if constexpr (std::is_same<T, CBLSPublicKey>()) {
assert(false);
}
return ::SerializeHash(v);
}
template <typename T>
[[nodiscard]] bool AddUniqueProperty(const CDeterministicMN& dmn, const T& v)
{
@ -417,7 +429,7 @@ private:
return false;
}
auto hash = ::SerializeHash(v);
auto hash = GetUniquePropertyHash(v);
auto oldEntry = mnUniquePropertyMap.find(hash);
if (oldEntry != nullptr && oldEntry->first != dmn.proTxHash) {
return false;
@ -437,7 +449,7 @@ private:
return false;
}
auto oldHash = ::SerializeHash(oldValue);
auto oldHash = GetUniquePropertyHash(oldValue);
auto p = mnUniquePropertyMap.find(oldHash);
if (p == nullptr || p->first != dmn.proTxHash) {
return false;
@ -466,6 +478,16 @@ private:
}
return true;
}
friend bool operator==(const CDeterministicMNList& a, const CDeterministicMNList& b)
{
return a.blockHash == b.blockHash &&
a.nHeight == b.nHeight &&
a.nTotalRegisteredCount == b.nTotalRegisteredCount &&
a.mnMap == b.mnMap &&
a.mnInternalIdMap == b.mnInternalIdMap &&
a.mnUniquePropertyMap == b.mnUniquePropertyMap;
}
};
class CDeterministicMNListDiff
@ -494,7 +516,7 @@ public:
}
template <typename Stream>
void Unserialize(Stream& s, const uint8_t format_version = CDeterministicMN::MN_TYPE_FORMAT)
void Unserialize(Stream& s, const uint8_t format_version = CDeterministicMN::MN_CURRENT_FORMAT)
{
updatedMNs.clear();
removedMns.clear();
@ -504,8 +526,6 @@ public:
tmp = ReadCompactSize(s);
for (size_t i = 0; i < tmp; i++) {
CDeterministicMN mn(0);
// Unserialise CDeterministicMN using CURRENT_MN_FORMAT and set it's type to the default value TYPE_REGULAR_MASTERNODE
// It will be later written with format MN_TYPE_FORMAT which includes the type field.
mn.Unserialize(s, format_version);
auto dmn = std::make_shared<CDeterministicMN>(mn);
addedMNs.push_back(dmn);
@ -534,10 +554,20 @@ public:
};
constexpr int llmq_max_blocks() {
int max_blocks{0};
for (const auto& llmq : Consensus::available_llmqs) {
int blocks = (llmq.useRotation ? 1 : llmq.signingActiveQuorumCount) * llmq.dkgInterval;
max_blocks = std::max(max_blocks, blocks);
}
return max_blocks;
}
class CDeterministicMNManager
{
static constexpr int DISK_SNAPSHOT_PERIOD = 576; // once per day
static constexpr int DISK_SNAPSHOTS = 3; // keep cache for 3 disk snapshots to have 2 full days covered
// keep cache for enough disk snapshots to have all active quourms covered
static constexpr int DISK_SNAPSHOTS = llmq_max_blocks() / DISK_SNAPSHOT_PERIOD + 1;
static constexpr int LIST_DIFFS_CACHE_SIZE = DISK_SNAPSHOT_PERIOD * DISK_SNAPSHOTS;
public:
@ -584,6 +614,7 @@ public:
bool IsDIP3Enforced(int nHeight = -1);
bool MigrateDBIfNeeded();
bool MigrateDBIfNeeded2();
void DoMaintenance();

View File

@ -26,16 +26,17 @@ std::string CDeterministicMNState::ToString() const
operatorPayoutAddress = EncodeDestination(dest);
}
return strprintf("CDeterministicMNState(nRegisteredHeight=%d, nLastPaidHeight=%d, nPoSePenalty=%d, nPoSeRevivedHeight=%d, nPoSeBanHeight=%d, nRevocationReason=%d, "
return strprintf("CDeterministicMNState(nVersion=%d, nRegisteredHeight=%d, nLastPaidHeight=%d, nPoSePenalty=%d, nPoSeRevivedHeight=%d, nPoSeBanHeight=%d, nRevocationReason=%d, "
"ownerAddress=%s, pubKeyOperator=%s, votingAddress=%s, addr=%s, payoutAddress=%s, operatorPayoutAddress=%s)",
nRegisteredHeight, nLastPaidHeight, nPoSePenalty, nPoSeRevivedHeight, nPoSeBanHeight, nRevocationReason,
EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.Get().ToString(), EncodeDestination(PKHash(keyIDVoting)), addr.ToStringIPPort(false), payoutAddress, operatorPayoutAddress);
nVersion, nRegisteredHeight, nLastPaidHeight, nPoSePenalty, nPoSeRevivedHeight, nPoSeBanHeight, nRevocationReason,
EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.ToString(), EncodeDestination(PKHash(keyIDVoting)), addr.ToStringIPPort(false), payoutAddress, operatorPayoutAddress);
}
void CDeterministicMNState::ToJson(UniValue& obj, MnType nType) const
{
obj.clear();
obj.setObject();
obj.pushKV("version", nVersion);
obj.pushKV("service", addr.ToStringIPPort(false));
obj.pushKV("registeredHeight", nRegisteredHeight);
obj.pushKV("lastPaidHeight", nLastPaidHeight);
@ -56,7 +57,7 @@ void CDeterministicMNState::ToJson(UniValue& obj, MnType nType) const
if (ExtractDestination(scriptPayout, dest)) {
obj.pushKV("payoutAddress", EncodeDestination(dest));
}
obj.pushKV("pubKeyOperator", pubKeyOperator.Get().ToString());
obj.pushKV("pubKeyOperator", pubKeyOperator.ToString());
if (ExtractDestination(scriptOperatorPayout, dest)) {
obj.pushKV("operatorPayoutAddress", EncodeDestination(dest));
}

View File

@ -72,6 +72,62 @@ public:
}
};
// TODO: To remove this in the future
class CDeterministicMNState_mntype_format
{
private:
int nPoSeBanHeight{-1};
friend class CDeterministicMNStateDiff;
friend class CDeterministicMNState;
public:
int nRegisteredHeight{-1};
int nLastPaidHeight{0};
int nConsecutivePayments{0};
int nPoSePenalty{0};
int nPoSeRevivedHeight{-1};
uint16_t nRevocationReason{CProUpRevTx::REASON_NOT_SPECIFIED};
uint256 confirmedHash;
uint256 confirmedHashWithProRegTxHash;
CKeyID keyIDOwner;
CBLSLazyPublicKey pubKeyOperator;
CKeyID keyIDVoting;
CService addr;
CScript scriptPayout;
CScript scriptOperatorPayout;
uint160 platformNodeID{};
uint16_t platformP2PPort{0};
uint16_t platformHTTPPort{0};
public:
CDeterministicMNState_mntype_format() = default;
SERIALIZE_METHODS(CDeterministicMNState_mntype_format, obj)
{
READWRITE(
obj.nRegisteredHeight,
obj.nLastPaidHeight,
obj.nConsecutivePayments,
obj.nPoSePenalty,
obj.nPoSeRevivedHeight,
obj.nPoSeBanHeight,
obj.nRevocationReason,
obj.confirmedHash,
obj.confirmedHashWithProRegTxHash,
obj.keyIDOwner,
obj.pubKeyOperator,
obj.keyIDVoting,
obj.addr,
obj.scriptPayout,
obj.scriptOperatorPayout,
obj.platformNodeID,
obj.platformP2PPort,
obj.platformHTTPPort);
}
};
class CDeterministicMNState
{
private:
@ -80,6 +136,8 @@ private:
friend class CDeterministicMNStateDiff;
public:
int nVersion{CProRegTx::LEGACY_BLS_VERSION};
int nRegisteredHeight{-1};
int nLastPaidHeight{0};
int nConsecutivePayments{0};
@ -107,7 +165,9 @@ public:
public:
CDeterministicMNState() = default;
explicit CDeterministicMNState(const CProRegTx& proTx) :
nVersion(proTx.nVersion),
keyIDOwner(proTx.keyIDOwner),
pubKeyOperator(proTx.pubKeyOperator),
keyIDVoting(proTx.keyIDVoting),
addr(proTx.addr),
scriptPayout(proTx.scriptPayout),
@ -115,7 +175,6 @@ public:
platformP2PPort(proTx.platformP2PPort),
platformHTTPPort(proTx.platformHTTPPort)
{
pubKeyOperator.Set(proTx.pubKeyOperator);
}
explicit CDeterministicMNState(const CDeterministicMNState_Oldformat& s) :
nPoSeBanHeight(s.nPoSeBanHeight),
@ -132,6 +191,27 @@ public:
addr(s.addr),
scriptPayout(s.scriptPayout),
scriptOperatorPayout(s.scriptOperatorPayout) {}
explicit CDeterministicMNState(const CDeterministicMNState_mntype_format& s) :
nPoSeBanHeight(s.nPoSeBanHeight),
nRegisteredHeight(s.nRegisteredHeight),
nLastPaidHeight(s.nLastPaidHeight),
nConsecutivePayments(s.nConsecutivePayments),
nPoSePenalty(s.nPoSePenalty),
nPoSeRevivedHeight(s.nPoSeRevivedHeight),
nRevocationReason(s.nRevocationReason),
confirmedHash(s.confirmedHash),
confirmedHashWithProRegTxHash(s.confirmedHashWithProRegTxHash),
keyIDOwner(s.keyIDOwner),
pubKeyOperator(s.pubKeyOperator),
keyIDVoting(s.keyIDVoting),
addr(s.addr),
scriptPayout(s.scriptPayout),
scriptOperatorPayout(s.scriptOperatorPayout),
platformNodeID(s.platformNodeID),
platformP2PPort(s.platformP2PPort),
platformHTTPPort(s.platformHTTPPort) {}
template <typename Stream>
CDeterministicMNState(deserialize_type, Stream& s)
{
@ -141,6 +221,7 @@ public:
SERIALIZE_METHODS(CDeterministicMNState, obj)
{
READWRITE(
obj.nVersion,
obj.nRegisteredHeight,
obj.nLastPaidHeight,
obj.nConsecutivePayments,
@ -150,8 +231,9 @@ public:
obj.nRevocationReason,
obj.confirmedHash,
obj.confirmedHashWithProRegTxHash,
obj.keyIDOwner,
obj.pubKeyOperator,
obj.keyIDOwner);
READWRITE(CBLSLazyPublicKeyVersionWrapper(const_cast<CBLSLazyPublicKey&>(obj.pubKeyOperator), obj.nVersion == CProRegTx::LEGACY_BLS_VERSION));
READWRITE(
obj.keyIDVoting,
obj.addr,
obj.scriptPayout,
@ -163,7 +245,8 @@ public:
void ResetOperatorFields()
{
pubKeyOperator.Set(CBLSPublicKey());
nVersion = CProRegTx::LEGACY_BLS_VERSION;
pubKeyOperator = CBLSLazyPublicKey();
addr = CService();
scriptOperatorPayout = CScript();
nRevocationReason = CProUpRevTx::REASON_NOT_SPECIFIED;
@ -225,6 +308,7 @@ public:
Field_platformNodeID = 0x8000,
Field_platformP2PPort = 0x10000,
Field_platformHTTPPort = 0x20000,
Field_nVersion = 0x40000,
};
#define DMN_STATE_DIFF_ALL_FIELDS \
@ -245,7 +329,8 @@ public:
DMN_STATE_DIFF_LINE(nConsecutivePayments) \
DMN_STATE_DIFF_LINE(platformNodeID) \
DMN_STATE_DIFF_LINE(platformP2PPort) \
DMN_STATE_DIFF_LINE(platformHTTPPort)
DMN_STATE_DIFF_LINE(platformHTTPPort) \
DMN_STATE_DIFF_LINE(nVersion)
public:
uint32_t fields{0};
@ -259,19 +344,26 @@ public:
#define DMN_STATE_DIFF_LINE(f) if (a.f != b.f) { state.f = b.f; fields |= Field_##f; }
DMN_STATE_DIFF_ALL_FIELDS
#undef DMN_STATE_DIFF_LINE
if (fields & Field_pubKeyOperator) { state.nVersion = b.nVersion; fields |= Field_nVersion; }
}
SERIALIZE_METHODS(CDeterministicMNStateDiff, obj)
{
// NOTE: reading pubKeyOperator requires nVersion
bool read_pubkey{false};
READWRITE(VARINT(obj.fields));
#define DMN_STATE_DIFF_LINE(f) \
if (strcmp(#f, "pubKeyOperator") == 0 && (obj.fields & Field_pubKeyOperator)) {\
/* TODO: implement migration to Basic BLS after the fork */ \
READWRITE(CBLSLazyPublicKeyVersionWrapper(const_cast<CBLSLazyPublicKey&>(obj.state.pubKeyOperator), true)); \
SER_READ(obj, read_pubkey = true); \
READWRITE(CBLSLazyPublicKeyVersionWrapper(const_cast<CBLSLazyPublicKey&>(obj.state.pubKeyOperator), obj.state.nVersion == CProRegTx::LEGACY_BLS_VERSION)); \
} else if (obj.fields & Field_##f) READWRITE(obj.state.f);
DMN_STATE_DIFF_ALL_FIELDS
#undef DMN_STATE_DIFF_LINE
if (read_pubkey) {
SER_READ(obj, obj.fields |= Field_nVersion);
SER_READ(obj, obj.state.pubKeyOperator.SetLegacy(obj.state.nVersion == CProRegTx::LEGACY_BLS_VERSION));
}
}
void ApplyToState(CDeterministicMNState& target) const

View File

@ -12,7 +12,8 @@
// "b_b" was used in the initial version of deterministic MN storage
// "b_b2" was used after compact diffs were introduced
// "b_b3" was used after masternode type introduction in evoDB
static const std::string EVODB_BEST_BLOCK = "b_b3";
// "b_b4" was used after storing protx version for each masternode in evoDB
static const std::string EVODB_BEST_BLOCK = "b_b4";
class CEvoDB;

View File

@ -23,9 +23,12 @@ maybe_error CProRegTx::IsTriviallyValid(bool is_bls_legacy_scheme) const
return {ValidationInvalidReason::CONSENSUS, "bad-protx-mode"};
}
if (keyIDOwner.IsNull() || !pubKeyOperator.IsValid() || keyIDVoting.IsNull()) {
if (keyIDOwner.IsNull() || !pubKeyOperator.Get().IsValid() || keyIDVoting.IsNull()) {
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-key-null"};
}
if (pubKeyOperator.IsLegacy() != (nVersion == LEGACY_BLS_VERSION)) {
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-operator-pubkey"};
}
if (!scriptPayout.IsPayToPublicKeyHash() && !scriptPayout.IsPayToScriptHash()) {
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-payee"};
}
@ -81,7 +84,7 @@ std::string CProRegTx::ToString() const
}
return strprintf("CProRegTx(nVersion=%d, nType=%d, collateralOutpoint=%s, addr=%s, nOperatorReward=%f, ownerAddress=%s, pubKeyOperator=%s, votingAddress=%s, scriptPayout=%s, platformNodeID=%s, platformP2PPort=%d, platformHTTPPort=%d)",
nVersion, ToUnderlying(nType), collateralOutpoint.ToStringShort(), addr.ToString(), (double)nOperatorReward / 100, EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.ToString(nVersion == LEGACY_BLS_VERSION), EncodeDestination(PKHash(keyIDVoting)), payee, platformNodeID.ToString(), platformP2PPort, platformHTTPPort);
nVersion, ToUnderlying(nType), collateralOutpoint.ToStringShort(), addr.ToString(), (double)nOperatorReward / 100, EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.ToString(), EncodeDestination(PKHash(keyIDVoting)), payee, platformNodeID.ToString(), platformP2PPort, platformHTTPPort);
}
maybe_error CProUpServTx::IsTriviallyValid(bool is_bls_legacy_scheme) const
@ -114,9 +117,12 @@ maybe_error CProUpRegTx::IsTriviallyValid(bool is_bls_legacy_scheme) const
return {ValidationInvalidReason::CONSENSUS, "bad-protx-mode"};
}
if (!pubKeyOperator.IsValid() || keyIDVoting.IsNull()) {
if (!pubKeyOperator.Get().IsValid() || keyIDVoting.IsNull()) {
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-key-null"};
}
if (pubKeyOperator.IsLegacy() != (nVersion == LEGACY_BLS_VERSION)) {
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-operator-pubkey"};
}
if (!scriptPayout.IsPayToPublicKeyHash() && !scriptPayout.IsPayToScriptHash()) {
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-payee"};
}
@ -132,7 +138,7 @@ std::string CProUpRegTx::ToString() const
}
return strprintf("CProUpRegTx(nVersion=%d, proTxHash=%s, pubKeyOperator=%s, votingAddress=%s, payoutAddress=%s)",
nVersion, proTxHash.ToString(), pubKeyOperator.ToString(nVersion == LEGACY_BLS_VERSION), EncodeDestination(PKHash(keyIDVoting)), payee);
nVersion, proTxHash.ToString(), pubKeyOperator.ToString(), EncodeDestination(PKHash(keyIDVoting)), payee);
}
maybe_error CProUpRevTx::IsTriviallyValid(bool is_bls_legacy_scheme) const

View File

@ -43,7 +43,7 @@ public:
uint16_t platformP2PPort{0};
uint16_t platformHTTPPort{0};
CKeyID keyIDOwner;
CBLSPublicKey pubKeyOperator;
CBLSLazyPublicKey pubKeyOperator;
CKeyID keyIDVoting;
uint16_t nOperatorReward{0};
CScript scriptPayout;
@ -65,7 +65,7 @@ public:
obj.collateralOutpoint,
obj.addr,
obj.keyIDOwner,
CBLSPublicKeyVersionWrapper(const_cast<CBLSPublicKey&>(obj.pubKeyOperator), (obj.nVersion == LEGACY_BLS_VERSION)),
CBLSLazyPublicKeyVersionWrapper(const_cast<CBLSLazyPublicKey&>(obj.pubKeyOperator), (obj.nVersion == LEGACY_BLS_VERSION)),
obj.keyIDVoting,
obj.nOperatorReward,
obj.scriptPayout,
@ -104,7 +104,7 @@ public:
if (ExtractDestination(scriptPayout, dest)) {
obj.pushKV("payoutAddress", EncodeDestination(dest));
}
obj.pushKV("pubKeyOperator", pubKeyOperator.ToString(nVersion == LEGACY_BLS_VERSION));
obj.pushKV("pubKeyOperator", pubKeyOperator.ToString());
obj.pushKV("operatorReward", (double)nOperatorReward / 100);
if (nType == MnType::HighPerformance) {
obj.pushKV("platformNodeID", platformNodeID.ToString());
@ -212,7 +212,7 @@ public:
uint16_t nVersion{LEGACY_BLS_VERSION}; // message version
uint256 proTxHash;
uint16_t nMode{0}; // only 0 supported for now
CBLSPublicKey pubKeyOperator;
CBLSLazyPublicKey pubKeyOperator;
CKeyID keyIDVoting;
CScript scriptPayout;
uint256 inputsHash; // replay protection
@ -230,7 +230,7 @@ public:
READWRITE(
obj.proTxHash,
obj.nMode,
CBLSPublicKeyVersionWrapper(const_cast<CBLSPublicKey&>(obj.pubKeyOperator), (obj.nVersion == LEGACY_BLS_VERSION)),
CBLSLazyPublicKeyVersionWrapper(const_cast<CBLSLazyPublicKey&>(obj.pubKeyOperator), (obj.nVersion == LEGACY_BLS_VERSION)),
obj.keyIDVoting,
obj.scriptPayout,
obj.inputsHash
@ -255,7 +255,7 @@ public:
if (ExtractDestination(scriptPayout, dest)) {
obj.pushKV("payoutAddress", EncodeDestination(dest));
}
obj.pushKV("pubKeyOperator", pubKeyOperator.ToString(nVersion == LEGACY_BLS_VERSION));
obj.pushKV("pubKeyOperator", pubKeyOperator.ToString());
obj.pushKV("inputsHash", inputsHash.ToString());
}

View File

@ -32,6 +32,7 @@ CSimplifiedMNListEntry::CSimplifiedMNListEntry(const CDeterministicMN& dmn) :
isValid(!dmn.pdmnState->IsBanned()),
scriptPayout(dmn.pdmnState->scriptPayout),
scriptOperatorPayout(dmn.pdmnState->scriptOperatorPayout),
nVersion(dmn.pdmnState->nVersion == CProRegTx::LEGACY_BLS_VERSION ? LEGACY_BLS_VERSION : BASIC_BLS_VERSION),
nType(dmn.nType),
platformHTTPPort(dmn.pdmnState->platformHTTPPort),
platformNodeID(dmn.pdmnState->platformNodeID)
@ -57,18 +58,19 @@ std::string CSimplifiedMNListEntry::ToString() const
operatorPayoutAddress = EncodeDestination(dest);
}
return strprintf("CSimplifiedMNListEntry(nType=%d, proRegTxHash=%s, confirmedHash=%s, service=%s, pubKeyOperator=%s, votingAddress=%s, isValid=%d, payoutAddress=%s, operatorPayoutAddress=%s, platformHTTPPort=%d, platformNodeID=%s)",
ToUnderlying(nType), proRegTxHash.ToString(), confirmedHash.ToString(), service.ToString(false), pubKeyOperator.Get().ToString(), EncodeDestination(PKHash(keyIDVoting)), isValid, payoutAddress, operatorPayoutAddress, platformHTTPPort, platformNodeID.ToString());
return strprintf("CSimplifiedMNListEntry(nVersion=%d, nType=%d, proRegTxHash=%s, confirmedHash=%s, service=%s, pubKeyOperator=%s, votingAddress=%s, isValid=%d, payoutAddress=%s, operatorPayoutAddress=%s, platformHTTPPort=%d, platformNodeID=%s)",
nVersion, ToUnderlying(nType), proRegTxHash.ToString(), confirmedHash.ToString(), service.ToString(false), pubKeyOperator.ToString(), EncodeDestination(PKHash(keyIDVoting)), isValid, payoutAddress, operatorPayoutAddress, platformHTTPPort, platformNodeID.ToString());
}
void CSimplifiedMNListEntry::ToJson(UniValue& obj, bool extended) const
{
obj.clear();
obj.setObject();
obj.pushKV("nVersion", nVersion);
obj.pushKV("proRegTxHash", proRegTxHash.ToString());
obj.pushKV("confirmedHash", confirmedHash.ToString());
obj.pushKV("service", service.ToString(false));
obj.pushKV("pubKeyOperator", pubKeyOperator.Get().ToString());
obj.pushKV("pubKeyOperator", pubKeyOperator.ToString());
obj.pushKV("votingAddress", EncodeDestination(PKHash(keyIDVoting)));
obj.pushKV("isValid", isValid);
obj.pushKV("nVersion", nVersion);
@ -102,15 +104,13 @@ CSimplifiedMNList::CSimplifiedMNList(const std::vector<CSimplifiedMNListEntry>&
});
}
CSimplifiedMNList::CSimplifiedMNList(const CDeterministicMNList& dmnList, bool isV19Active)
CSimplifiedMNList::CSimplifiedMNList(const CDeterministicMNList& dmnList)
{
mnList.resize(dmnList.GetAllMNsCount());
size_t i = 0;
dmnList.ForEachMN(false, [this, &i, isV19Active](auto& dmn) {
auto sme = std::make_unique<CSimplifiedMNListEntry>(dmn);
sme->nVersion = isV19Active ? CSimplifiedMNListEntry::BASIC_BLS_VERSION : CSimplifiedMNListEntry::LEGACY_BLS_VERSION;
mnList[i++] = std::move(sme);
dmnList.ForEachMN(false, [this, &i](auto& dmn) {
mnList[i++] = std::make_unique<CSimplifiedMNListEntry>(dmn);
});
std::sort(mnList.begin(), mnList.end(), [&](const std::unique_ptr<CSimplifiedMNListEntry>& a, const std::unique_ptr<CSimplifiedMNListEntry>& b) {

View File

@ -39,7 +39,7 @@ public:
uint160 platformNodeID{};
CScript scriptPayout; // mem-only
CScript scriptOperatorPayout; // mem-only
uint16_t nVersion{LEGACY_BLS_VERSION}; // mem-only
uint16_t nVersion{LEGACY_BLS_VERSION};
CSimplifiedMNListEntry() = default;
explicit CSimplifiedMNListEntry(const CDeterministicMN& dmn);
@ -65,6 +65,9 @@ public:
SERIALIZE_METHODS(CSimplifiedMNListEntry, obj)
{
if ((s.GetType() & SER_NETWORK) && s.GetVersion() >= SMNLE_VERSIONED_PROTO_VERSION) {
READWRITE(obj.nVersion);
}
READWRITE(
obj.proRegTxHash,
obj.confirmedHash,
@ -98,7 +101,7 @@ public:
CSimplifiedMNList() = default;
explicit CSimplifiedMNList(const std::vector<CSimplifiedMNListEntry>& smlEntries);
explicit CSimplifiedMNList(const CDeterministicMNList& dmnList, bool isV19Active);
explicit CSimplifiedMNList(const CDeterministicMNList& dmnList);
uint256 CalcMerkleRoot(bool* pmutated = nullptr) const;
bool operator==(const CSimplifiedMNList& rhs) const;
@ -121,8 +124,7 @@ public:
class CSimplifiedMNListDiff
{
public:
static constexpr uint16_t LEGACY_BLS_VERSION = 1;
static constexpr uint16_t BASIC_BLS_VERSION = 2;
static constexpr uint16_t CURRENT_VERSION = 1;
uint256 baseBlockHash;
uint256 blockHash;
@ -130,7 +132,7 @@ public:
CTransactionRef cbTx;
std::vector<uint256> deletedMNs;
std::vector<CSimplifiedMNListEntry> mnList;
uint16_t nVersion{LEGACY_BLS_VERSION};
uint16_t nVersion{CURRENT_VERSION};
std::vector<std::pair<uint8_t, uint256>> deletedQuorums; // p<LLMQType, quorumHash>
std::vector<llmq::CFinalCommitment> newQuorums;

View File

@ -9,7 +9,7 @@
uint256 CalcTxInputsHash(const CTransaction& tx)
{
CHashWriter hw(CLIENT_VERSION, SER_GETHASH);
CHashWriter hw(SER_GETHASH, CLIENT_VERSION);
for (const auto& in : tx.vin) {
hw << in.prevout;
}

View File

@ -13,6 +13,7 @@
#include <hash.h>
#include <llmq/blockprocessor.h>
#include <llmq/commitment.h>
#include <llmq/utils.h>
#include <primitives/block.h>
#include <validation.h>
@ -152,6 +153,13 @@ bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, ll
int64_t nTime5 = GetTimeMicros();
nTimeMerkle += nTime5 - nTime4;
LogPrint(BCLog::BENCHMARK, " - CheckCbTxMerkleRoots: %.2fms [%.2fs]\n", 0.001 * (nTime5 - nTime4), nTimeMerkle * 0.000001);
if (llmq::utils::V19ActivationHeight(pindex) == pindex->nHeight + 1) {
// NOTE: The block next to the activation is the one that is using new rules.
// V19 activated just activated, so we must switch to the new rules here.
bls::bls_legacy_scheme.store(false);
LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load());
}
} catch (const std::exception& e) {
LogPrintf("%s -- failed: %s\n", __func__, e.what());
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "failed-procspectxsinblock");
@ -164,7 +172,16 @@ bool UndoSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, llmq:
{
AssertLockHeld(cs_main);
auto bls_legacy_scheme = bls::bls_legacy_scheme.load();
try {
if (llmq::utils::V19ActivationHeight(pindex) == pindex->nHeight + 1) {
// NOTE: The block next to the activation is the one that is using new rules.
// Removing the activation block here, so we must switch back to the old rules.
bls::bls_legacy_scheme.store(true);
LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load());
}
for (int i = (int)block.vtx.size() - 1; i >= 0; --i) {
const CTransaction& tx = *block.vtx[i];
if (!UndoSpecialTx(tx, pindex)) {
@ -180,6 +197,8 @@ bool UndoSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, llmq:
return false;
}
} catch (const std::exception& e) {
bls::bls_legacy_scheme.store(bls_legacy_scheme);
LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load());
return error(strprintf("%s -- failed: %s\n", __func__, e.what()).c_str());
}

View File

@ -316,7 +316,7 @@ bool CGovernanceObject::CheckSignature(const CBLSPublicKey& pubKey) const
{
CBLSSignature sig;
const auto pindex = llmq::utils::V19ActivationIndex(::ChainActive().Tip());
bool is_bls_legacy_scheme = pindex == nullptr || nTime < pindex->nTime;
bool is_bls_legacy_scheme = pindex == nullptr || nTime < pindex->pprev->nTime;
sig.SetByteVector(vchSig, is_bls_legacy_scheme);
if (!sig.VerifyInsecure(pubKey, GetSignatureHash(), is_bls_legacy_scheme)) {
LogPrintf("CGovernanceObject::CheckSignature -- VerifyInsecure() failed\n");
@ -501,7 +501,7 @@ bool CGovernanceObject::IsValidLocally(std::string& strError, bool& fMissingConf
// Check that we have a valid MN signature
if (!CheckSignature(dmn->pdmnState->pubKeyOperator.Get())) {
strError = "Invalid masternode signature for: " + strOutpoint + ", pubkey = " + dmn->pdmnState->pubKeyOperator.Get().ToString();
strError = "Invalid masternode signature for: " + strOutpoint + ", pubkey = " + dmn->pdmnState->pubKeyOperator.ToString();
return false;
}

View File

@ -238,7 +238,7 @@ bool CGovernanceVote::CheckSignature(const CBLSPublicKey& pubKey) const
{
CBLSSignature sig;
const auto pindex = llmq::utils::V19ActivationIndex(::ChainActive().Tip());
bool is_bls_legacy_scheme = pindex == nullptr || nTime < pindex->nTime;
bool is_bls_legacy_scheme = pindex == nullptr || nTime < pindex->pprev->nTime;
sig.SetByteVector(vchSig, is_bls_legacy_scheme);
if (!sig.VerifyInsecure(pubKey, GetSignatureHash(), is_bls_legacy_scheme)) {
LogPrintf("CGovernanceVote::CheckSignature -- VerifyInsecure() failed\n");

View File

@ -734,14 +734,14 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-pushversion", "Protocol version to report to other nodes", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-shrinkdebugfile", "Shrink debug.log file on client startup (default: 1 when no -debug)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-sporkaddr=<dashaddress>", "Override spork address. Only useful for regtest and devnet. Using this on mainnet or testnet will ban you.", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-sporkkey=<privatekey>", "Set the private key to be used for signing spork messages.", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-sporkkey=<privatekey>", "Set the private key to be used for signing spork messages.", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-uacomment=<cmt>", "Append comment to the user agent string", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
SetupChainParamsBaseOptions(argsman);
argsman.AddArg("-llmq-data-recovery=<n>", strprintf("Enable automated quorum data recovery (default: %u)", llmq::DEFAULT_ENABLE_QUORUM_DATA_RECOVERY), ArgsManager::ALLOW_ANY, OptionsCategory::MASTERNODE);
argsman.AddArg("-llmq-qvvec-sync=<quorum_name>:<mode>", strprintf("Defines from which LLMQ type the masternode should sync quorum verification vectors. Can be used multiple times with different LLMQ types. <mode>: %d (sync always from all quorums of the type defined by <quorum_name>), %d (sync from all quorums of the type defined by <quorum_name> if a member of any of the quorums)", (int32_t)llmq::QvvecSyncMode::Always, (int32_t)llmq::QvvecSyncMode::OnlyIfTypeMember), ArgsManager::ALLOW_ANY, OptionsCategory::MASTERNODE);
argsman.AddArg("-masternodeblsprivkey=<hex>", "Set the masternode BLS private key and enable the client to act as a masternode", ArgsManager::ALLOW_ANY, OptionsCategory::MASTERNODE);
argsman.AddArg("-masternodeblsprivkey=<hex>", "Set the masternode BLS private key and enable the client to act as a masternode", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::MASTERNODE);
argsman.AddArg("-platform-user=<user>", "Set the username for the \"platform user\", a restricted user intended to be used by Dash Platform, to the specified username.", ArgsManager::ALLOW_ANY, OptionsCategory::MASTERNODE);
argsman.AddArg("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !testnetChainParams->RequireStandard()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
@ -1702,7 +1702,10 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc
activeMasternodeInfo.blsKeyOperator = std::make_unique<CBLSSecretKey>(keyOperator);
activeMasternodeInfo.blsPubKeyOperator = std::make_unique<CBLSPublicKey>(keyOperator.GetPublicKey());
}
LogPrintf("MASTERNODE:\n blsPubKeyOperator: %s\n", activeMasternodeInfo.blsPubKeyOperator->ToString());
// We don't know the actual scheme at this point, print both
LogPrintf("MASTERNODE:\n blsPubKeyOperator legacy: %s\n blsPubKeyOperator basic: %s\n",
activeMasternodeInfo.blsPubKeyOperator->ToString(true),
activeMasternodeInfo.blsPubKeyOperator->ToString(false));
} else {
LOCK(activeMasternodeInfoCs);
activeMasternodeInfo.blsKeyOperator = std::make_unique<CBLSSecretKey>();
@ -2016,6 +2019,8 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc
LOCK(cs_main);
node.evodb.reset();
node.evodb = std::make_unique<CEvoDB>(nEvoDbCache, false, fReset || fReindexChainState);
chainman.Reset();
chainman.InitializeChainstate(llmq::chainLocksHandler, llmq::quorumInstantSendManager, llmq::quorumBlockProcessor, node.evodb, *Assert(node.mempool));
chainman.m_total_coinstip_cache = nCoinCacheUsage;
chainman.m_total_coinsdb_cache = nCoinDBCache;
@ -2168,6 +2173,10 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc
strLoadError = _("Error upgrading evo database");
break;
}
if (!deterministicMNManager->MigrateDBIfNeeded2()) {
strLoadError = _("Error upgrading evo database");
break;
}
if (!llmq::quorumBlockProcessor->UpgradeDB()) {
strLoadError = _("Error upgrading evo database");
@ -2192,8 +2201,11 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc
break;
}
if (llmq::utils::IsV19Active(tip))
bool v19active = llmq::utils::IsV19Active(tip);
if (llmq::utils::IsV19Active(tip)) {
bls::bls_legacy_scheme.store(false);
LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load());
}
// Only verify the DB of the active chainstate. This is fixed in later
// work when we allow VerifyDB to be parameterized by chainstate.
@ -2207,6 +2219,14 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc
failed_verification = true;
break;
}
// VerifyDB() disconnects blocks which might result in us switching back to legacy.
// Make sure we use the right scheme.
if (v19active && bls::bls_legacy_scheme.load()) {
bls::bls_legacy_scheme.store(false);
LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load());
}
} else {
// TODO: CEvoDB instance should probably be a part of CChainState
// (for multiple chainstates to actually work in parallel)

View File

@ -146,9 +146,6 @@ bool CQuorumBlockProcessor::ProcessBlock(const CBlock& block, const CBlockIndex*
return true;
}
if (utils::IsV19Active(pindex->pprev))
bls::bls_legacy_scheme.store(false);
llmq::utils::PreComputeQuorumMembers(pindex);
std::multimap<Consensus::LLMQType, CFinalCommitment> qcs;
@ -307,9 +304,6 @@ bool CQuorumBlockProcessor::UndoBlock(const CBlock& block, const CBlockIndex* pi
{
AssertLockHeld(cs_main);
if (!utils::IsV19Active(pindex->pprev))
bls::bls_legacy_scheme.store(true);
llmq::utils::PreComputeQuorumMembers(pindex, true);
std::multimap<Consensus::LLMQType, CFinalCommitment> qcs;

View File

@ -247,6 +247,10 @@ void CChainLocksHandler::TrySignChainTip()
return;
}
if (!ChainLocksSigningEnabled(spork_manager)) {
return;
}
const CBlockIndex* pindex = WITH_LOCK(cs_main, return ::ChainActive().Tip());
if (pindex->pprev == nullptr) {
@ -678,4 +682,9 @@ bool AreChainLocksEnabled(const CSporkManager& sporkManager)
return sporkManager.IsSporkActive(SPORK_19_CHAINLOCKS_ENABLED);
}
bool ChainLocksSigningEnabled(const CSporkManager& sporkManager)
{
return sporkManager.GetSporkValue(SPORK_19_CHAINLOCKS_ENABLED) == 0;
}
} // namespace llmq

View File

@ -119,6 +119,7 @@ private:
extern std::unique_ptr<CChainLocksHandler> chainLocksHandler;
bool AreChainLocksEnabled(const CSporkManager& sporkManager);
bool ChainLocksSigningEnabled(const CSporkManager& sporkManager);
} // namespace llmq

View File

@ -74,7 +74,7 @@ public:
CRecoveredSig(Consensus::LLMQType _llmqType, const uint256& _quorumHash, const uint256& _id, const uint256& _msgHash, const CBLSLazySignature& _sig) :
CSigBase(_llmqType, _quorumHash, _id, _msgHash), sig(_sig) {UpdateHash();};
CRecoveredSig(Consensus::LLMQType _llmqType, const uint256& _quorumHash, const uint256& _id, const uint256& _msgHash, const CBLSSignature& _sig) :
CSigBase(_llmqType, _quorumHash, _id, _msgHash) {const_cast<CBLSLazySignature&>(sig).Set(_sig); UpdateHash();};
CSigBase(_llmqType, _quorumHash, _id, _msgHash) {const_cast<CBLSLazySignature&>(sig).Set(_sig, bls::bls_legacy_scheme.load()); UpdateHash();};
private:
// only in-memory

View File

@ -1514,7 +1514,7 @@ std::optional<CSigShare> CSigSharesManager::CreateSigShare(const CQuorumCPtr& qu
CSigShare sigShare(quorum->params.type, quorum->qc->quorumHash, id, msgHash, uint16_t(memberIdx), {});
uint256 signHash = sigShare.buildSignHash();
sigShare.sigShare.Set(skShare.Sign(signHash));
sigShare.sigShare.Set(skShare.Sign(signHash), bls::bls_legacy_scheme.load());
if (!sigShare.sigShare.Get().IsValid()) {
LogPrintf("CSigSharesManager::%s -- failed to sign sigShare. signHash=%s, id=%s, msgHash=%s, time=%s\n", __func__,
signHash.ToString(), sigShare.getId().ToString(), sigShare.getMsgHash().ToString(), t.count());

View File

@ -573,7 +573,7 @@ uint256 BuildCommitmentHash(Consensus::LLMQType llmqType, const uint256& blockHa
const std::vector<bool>& validMembers, const CBLSPublicKey& pubKey,
const uint256& vvecHash)
{
CHashWriter hw(SER_NETWORK, 0);
CHashWriter hw(SER_GETHASH, 0);
hw << llmqType;
hw << blockHash;
hw << DYNBITSET(validMembers);
@ -659,16 +659,21 @@ bool IsV19Active(const CBlockIndex* pindex)
return VersionBitsState(pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_V19, llmq_versionbitscache) == ThresholdState::ACTIVE;
}
const CBlockIndex* V19ActivationIndex(const CBlockIndex* pindex)
const int V19ActivationHeight(const CBlockIndex* pindex)
{
assert(pindex);
LOCK(cs_llmq_vbc);
if (VersionBitsState(pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_V19, llmq_versionbitscache) != ThresholdState::ACTIVE) {
return nullptr;
}
int nHeight = VersionBitsStateSinceHeight(pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_V19, llmq_versionbitscache);
return pindex->GetAncestor(nHeight);
LOCK(cs_llmq_vbc);
if (VersionBitsState(pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_V19, llmq_versionbitscache) != ThresholdState::ACTIVE) {
return -1;
}
return VersionBitsStateSinceHeight(pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_V19, llmq_versionbitscache);
}
const CBlockIndex* V19ActivationIndex(const CBlockIndex* pindex)
{
assert(pindex);
return pindex->GetAncestor(V19ActivationHeight(pindex));
}
bool IsInstantSendLLMQTypeShared()

View File

@ -84,6 +84,7 @@ Consensus::LLMQType GetInstantSendLLMQType(const CQuorumManager& qman, const CBl
Consensus::LLMQType GetInstantSendLLMQType(bool deterministic);
bool IsDIP0024Active(const CBlockIndex* pindex);
bool IsV19Active(const CBlockIndex* pindex);
const int V19ActivationHeight(const CBlockIndex* pindex);
const CBlockIndex* V19ActivationIndex(const CBlockIndex* pindex);
/// Returns the state of `-llmq-data-recovery`

View File

@ -131,6 +131,7 @@ void CActiveMasternodeManager::Init(const CBlockIndex* pindex)
activeMasternodeInfo.proTxHash = dmn->proTxHash;
activeMasternodeInfo.outpoint = dmn->collateralOutpoint;
activeMasternodeInfo.legacy = dmn->pdmnState->nVersion == CProRegTx::LEGACY_BLS_VERSION;
state = MASTERNODE_READY;
}

View File

@ -28,6 +28,7 @@ struct CActiveMasternodeInfo {
uint256 proTxHash;
COutPoint outpoint;
CService service;
bool legacy{true};
};

View File

@ -181,6 +181,10 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead
// Note: GetNextWorkRequiredBTC has it's own special difficulty rule,
// so we only apply this to post-BTC algos.
if (params.fPowNoRetargeting) {
return bnPowLimit.GetCompact();
}
if (params.fPowAllowMinDifficultyBlocks) {
// recent block is more than 2 hours old
if (pblock->GetBlockTime() > pindexLast->GetBlockTime() + 2 * 60 * 60) {

View File

@ -460,7 +460,7 @@ void BitcoinGUI::createActions()
// Jump directly to tabs in RPC-console
connect(openInfoAction, &QAction::triggered, this, &BitcoinGUI::showInfo);
connect(openRPCConsoleAction, &QAction::triggered, this, &BitcoinGUI::showDebugWindow);
connect(openRPCConsoleAction, &QAction::triggered, this, &BitcoinGUI::showConsole);
connect(openGraphAction, &QAction::triggered, this, &BitcoinGUI::showGraph);
connect(openPeersAction, &QAction::triggered, this, &BitcoinGUI::showPeers);
connect(openRepairAction, &QAction::triggered, this, &BitcoinGUI::showRepair);

View File

@ -38,7 +38,8 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
ui(new Ui::OptionsDialog),
model(nullptr),
mapper(nullptr),
pageButtons(nullptr)
pageButtons(nullptr),
m_enable_wallet(enableWallet)
{
ui->setupUi(this);
@ -108,7 +109,7 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
pageButtons = new QButtonGroup(this);
pageButtons->addButton(ui->btnMain, pageButtons->buttons().size());
/* Remove Wallet/CoinJoin tabs and 3rd party-URL textbox in case of -disablewallet */
if (!enableWallet) {
if (!m_enable_wallet) {
ui->stackedWidgetOptions->removeWidget(ui->pageWallet);
ui->btnWallet->hide();
ui->stackedWidgetOptions->removeWidget(ui->pageCoinJoin);
@ -396,9 +397,11 @@ void OptionsDialog::on_okButton_clicked()
mapper->submit();
appearance->accept();
#ifdef ENABLE_WALLET
for (auto& wallet : model->node().walletClient().getWallets()) {
wallet->coinJoin().resetCachedBlocks();
wallet->markDirty();
if (m_enable_wallet) {
for (auto& wallet : model->node().walletClient().getWallets()) {
wallet->coinJoin().resetCachedBlocks();
wallet->markDirty();
}
}
#endif // ENABLE_WALLET
accept();

View File

@ -89,6 +89,7 @@ private:
QString previousTheme;
AppearanceWidget* appearance;
bool fCoinJoinEnabledPrev{false};
bool m_enable_wallet{false};
void showEvent(QShowEvent* event) override;
};

View File

@ -86,6 +86,7 @@ SendCoinsDialog::SendCoinsDialog(bool _fCoinJoin, QWidget* parent) :
GUIUtil::setFont({ui->labelCoinControlFeatures
}, GUIUtil::FontWeight::Bold, 16);
ui->checkBoxCoinControlChange->setEnabled(!_fCoinJoin);
GUIUtil::setupAddressWidget(ui->lineEditCoinControlChange, this);
addEntry();

View File

@ -184,9 +184,7 @@ static CKeyID ParsePubKeyIDFromAddress(const std::string& strAddress, const std:
static CBLSPublicKey ParseBLSPubKey(const std::string& hexKey, const std::string& paramName, bool specific_legacy_bls_scheme = false)
{
CBLSPublicKey pubKey;
bool is_bls_legacy_scheme = !llmq::utils::IsV19Active(::ChainActive().Tip());
bool use_bls_scheme = specific_legacy_bls_scheme || is_bls_legacy_scheme;
if (!pubKey.SetHexStr(hexKey, use_bls_scheme)) {
if (!pubKey.SetHexStr(hexKey, specific_legacy_bls_scheme)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be a valid BLS public key, not %s", paramName, hexKey));
}
return pubKey;
@ -609,7 +607,7 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request,
}
bool isV19active = llmq::utils::IsV19Active(WITH_LOCK(cs_main, return ::ChainActive().Tip();));
if (mnType == MnType::HighPerformance && !isV19active) {
if (isHPMNrequested && !isV19active) {
throw JSONRPCError(RPC_INVALID_REQUEST, "HPMN aren't allowed yet");
}
@ -619,12 +617,9 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request,
tx.nVersion = 3;
tx.nType = TRANSACTION_PROVIDER_REGISTER;
const bool use_legacy = isV19active ? specific_legacy_bls_scheme : true;
CProRegTx ptx;
if (specific_legacy_bls_scheme && !isHPMNrequested) {
ptx.nVersion = CProRegTx::LEGACY_BLS_VERSION;
} else {
ptx.nVersion = CProRegTx::GetVersion(isV19active);
}
ptx.nType = mnType;
if (isFundRegister) {
@ -661,7 +656,10 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request,
}
ptx.keyIDOwner = ParsePubKeyIDFromAddress(request.params[paramIdx + 1].get_str(), "owner address");
CBLSPublicKey pubKeyOperator = ParseBLSPubKey(request.params[paramIdx + 2].get_str(), "operator BLS address", specific_legacy_bls_scheme && !isHPMNrequested);
ptx.pubKeyOperator.Set(ParseBLSPubKey(request.params[paramIdx + 2].get_str(), "operator BLS address", use_legacy), use_legacy);
ptx.nVersion = use_legacy ? CProRegTx::LEGACY_BLS_VERSION : CProRegTx::BASIC_BLS_VERSION;
CHECK_NONFATAL(ptx.pubKeyOperator.IsLegacy() == (ptx.nVersion == CProRegTx::LEGACY_BLS_VERSION));
CKeyID keyIDVoting = ptx.keyIDOwner;
if (request.params[paramIdx + 3].get_str() != "") {
@ -703,7 +701,6 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request,
paramIdx += 3;
}
ptx.pubKeyOperator = pubKeyOperator;
ptx.keyIDVoting = keyIDVoting;
ptx.scriptPayout = GetScriptForDestination(payoutDest);
@ -905,7 +902,6 @@ static UniValue protx_update_service_common_wrapper(const JSONRPCRequest& reques
}
CProUpServTx ptx;
ptx.nVersion = CProUpServTx::GetVersion(llmq::utils::IsV19Active(::ChainActive().Tip()));
ptx.nType = mnType;
ptx.proTxHash = ParseHashV(request.params[0], "proTxHash");
@ -944,6 +940,7 @@ static UniValue protx_update_service_common_wrapper(const JSONRPCRequest& reques
if (dmn->nType != mnType) {
throw std::runtime_error(strprintf("masternode with proTxHash %s is not a %s", ptx.proTxHash.ToString(), GetMnType(mnType).description));
}
ptx.nVersion = dmn->pdmnState->nVersion;
if (keyOperator.GetPublicKey() != dmn->pdmnState->pubKeyOperator.Get()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("the operator key does not belong to the registered public key"));
@ -1030,24 +1027,28 @@ static UniValue protx_update_registrar_wrapper(const JSONRPCRequest& request, co
EnsureWalletIsUnlocked(wallet.get());
CProUpRegTx ptx;
if (specific_legacy_bls_scheme) {
ptx.nVersion = CProUpRegTx::LEGACY_BLS_VERSION;
} else {
ptx.nVersion = CProUpRegTx::GetVersion(llmq::utils::IsV19Active(::ChainActive().Tip()));
}
ptx.proTxHash = ParseHashV(request.params[0], "proTxHash");
auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(ptx.proTxHash);
if (!dmn) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("masternode %s not found", ptx.proTxHash.ToString()));
}
ptx.pubKeyOperator = dmn->pdmnState->pubKeyOperator.Get();
ptx.keyIDVoting = dmn->pdmnState->keyIDVoting;
ptx.scriptPayout = dmn->pdmnState->scriptPayout;
const bool use_legacy = llmq::utils::IsV19Active(::ChainActive().Tip()) ? specific_legacy_bls_scheme : true;
if (request.params[1].get_str() != "") {
ptx.pubKeyOperator = ParseBLSPubKey(request.params[1].get_str(), "operator BLS address", specific_legacy_bls_scheme);
// new pubkey
ptx.pubKeyOperator.Set(ParseBLSPubKey(request.params[1].get_str(), "operator BLS address", use_legacy), use_legacy);
} else {
// same pubkey, reuse as is
ptx.pubKeyOperator = dmn->pdmnState->pubKeyOperator;
}
ptx.nVersion = use_legacy ? CProUpRegTx::LEGACY_BLS_VERSION : CProUpRegTx::BASIC_BLS_VERSION;
CHECK_NONFATAL(ptx.pubKeyOperator.IsLegacy() == (ptx.nVersion == CProUpRegTx::LEGACY_BLS_VERSION));
if (request.params[2].get_str() != "") {
ptx.keyIDVoting = ParsePubKeyIDFromAddress(request.params[2].get_str(), "voting address");
}

View File

@ -323,7 +323,7 @@ static UniValue gobject_submit(const JSONRPCRequest& request)
bool fMnFound = WITH_LOCK(activeMasternodeInfoCs, return mnList.HasValidMNByCollateral(activeMasternodeInfo.outpoint));
LogPrint(BCLog::GOBJECT, "gobject_submit -- pubKeyOperator = %s, outpoint = %s, params.size() = %lld, fMnFound = %d\n",
(WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.blsPubKeyOperator ? activeMasternodeInfo.blsPubKeyOperator->ToString() : "N/A")),
(WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.blsPubKeyOperator ? activeMasternodeInfo.blsPubKeyOperator->ToString(activeMasternodeInfo.legacy) : "N/A")),
WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.outpoint.ToStringShort()), request.params.size(), fMnFound);
// ASSEMBLE NEW GOVERNANCE OBJECT FROM USER PARAMETERS

View File

@ -675,7 +675,7 @@ static UniValue masternodelist(const JSONRPCRequest& request)
EncodeDestination(PKHash(dmn.pdmnState->keyIDOwner)) << " " <<
EncodeDestination(PKHash(dmn.pdmnState->keyIDVoting)) << " " <<
collateralAddressStr << " " <<
dmn.pdmnState->pubKeyOperator.Get().ToString();
dmn.pdmnState->pubKeyOperator.ToString();
std::string strInfo = streamInfo.str();
if (strFilter !="" && strInfo.find(strFilter) == std::string::npos &&
strOutpoint.find(strFilter) == std::string::npos) return;
@ -697,7 +697,7 @@ static UniValue masternodelist(const JSONRPCRequest& request)
objMN.pushKV("owneraddress", EncodeDestination(PKHash(dmn.pdmnState->keyIDOwner)));
objMN.pushKV("votingaddress", EncodeDestination(PKHash(dmn.pdmnState->keyIDVoting)));
objMN.pushKV("collateraladdress", collateralAddressStr);
objMN.pushKV("pubkeyoperator", dmn.pdmnState->pubKeyOperator.Get().ToString());
objMN.pushKV("pubkeyoperator", dmn.pdmnState->pubKeyOperator.ToString());
obj.pushKV(strOutpoint, objMN);
} else if (strMode == "lastpaidblock") {
if (strFilter !="" && strOutpoint.find(strFilter) == std::string::npos) return;
@ -714,7 +714,7 @@ static UniValue masternodelist(const JSONRPCRequest& request)
obj.pushKV(strOutpoint, EncodeDestination(PKHash(dmn.pdmnState->keyIDOwner)));
} else if (strMode == "pubkeyoperator") {
if (strFilter !="" && strOutpoint.find(strFilter) == std::string::npos) return;
obj.pushKV(strOutpoint, dmn.pdmnState->pubKeyOperator.Get().ToString());
obj.pushKV(strOutpoint, dmn.pdmnState->pubKeyOperator.ToString());
} else if (strMode == "status") {
std::string strStatus = dmnToStatus(dmn);
if (strFilter !="" && strStatus.find(strFilter) == std::string::npos &&

View File

@ -210,7 +210,7 @@ static UniValue BuildQuorumInfo(const llmq::CQuorumCPtr& quorum, bool includeMem
UniValue mo(UniValue::VOBJ);
mo.pushKV("proTxHash", dmn->proTxHash.ToString());
mo.pushKV("service", dmn->pdmnState->addr.ToString());
mo.pushKV("pubKeyOperator", dmn->pdmnState->pubKeyOperator.Get().ToString());
mo.pushKV("pubKeyOperator", dmn->pdmnState->pubKeyOperator.ToString());
mo.pushKV("valid", quorum->qc->validMembers[i]);
if (quorum->qc->validMembers[i]) {
CBLSPublicKey pubKey = quorum->GetPubKeyShare(i);

View File

@ -125,7 +125,7 @@ static CMutableTransaction CreateProRegTx(const CTxMemPool& mempool, SimpleUTXOM
proTx.collateralOutpoint.n = 0;
proTx.addr = LookupNumeric("1.1.1.1", port);
proTx.keyIDOwner = ownerKeyRet.GetPubKey().GetID();
proTx.pubKeyOperator = operatorKeyRet.GetPublicKey();
proTx.pubKeyOperator.Set(operatorKeyRet.GetPublicKey(), bls::bls_legacy_scheme.load());
proTx.keyIDVoting = ownerKeyRet.GetPubKey().GetID();
proTx.scriptPayout = scriptPayout;

View File

@ -21,6 +21,7 @@
#include <evo/specialtx.h>
#include <evo/providertx.h>
#include <evo/deterministicmns.h>
#include <llmq/utils.h>
#include <boost/test/unit_test.hpp>
@ -103,7 +104,7 @@ static CMutableTransaction CreateProRegTx(const CTxMemPool& mempool, SimpleUTXOM
proTx.collateralOutpoint.n = 0;
proTx.addr = LookupNumeric("1.1.1.1", port);
proTx.keyIDOwner = ownerKeyRet.GetPubKey().GetID();
proTx.pubKeyOperator = operatorKeyRet.GetPublicKey();
proTx.pubKeyOperator.Set(operatorKeyRet.GetPublicKey(), bls::bls_legacy_scheme.load());
proTx.keyIDVoting = ownerKeyRet.GetPubKey().GetID();
proTx.scriptPayout = scriptPayout;
@ -143,7 +144,7 @@ static CMutableTransaction CreateProUpRegTx(const CTxMemPool& mempool, SimpleUTX
CProUpRegTx proTx;
proTx.nVersion = CProUpRegTx::GetVersion(!bls::bls_legacy_scheme);
proTx.proTxHash = proTxHash;
proTx.pubKeyOperator = pubKeyOperator;
proTx.pubKeyOperator.Set(pubKeyOperator, bls::bls_legacy_scheme.load());
proTx.keyIDVoting = keyIDVoting;
proTx.scriptPayout = scriptPayout;
@ -264,6 +265,136 @@ void FuncDIP3Activation(TestChainSetup& setup)
BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash()));
};
void FuncV19Activation(TestChainSetup& setup)
{
BOOST_ASSERT(!llmq::utils::IsV19Active(::ChainActive().Tip()));
// create
auto utxos = BuildSimpleUtxoMap(setup.m_coinbase_txns);
CKey owner_key;
CBLSSecretKey operator_key;
CKey collateral_key;
collateral_key.MakeNewKey(false);
auto collateralScript = GetScriptForDestination(PKHash(collateral_key.GetPubKey()));
auto tx_reg = CreateProRegTx(*(setup.m_node.mempool), utxos, 1, collateralScript, setup.coinbaseKey, owner_key, operator_key);
auto tx_reg_hash = tx_reg.GetHash();
int nHeight = ::ChainActive().Height();
auto block = std::make_shared<CBlock>(setup.CreateBlock({tx_reg}, setup.coinbaseKey));
BOOST_ASSERT(Assert(setup.m_node.chainman)->ProcessNewBlock(Params(), block, true, nullptr));
BOOST_ASSERT(!llmq::utils::IsV19Active(::ChainActive().Tip()));
++nHeight;
BOOST_CHECK_EQUAL(::ChainActive().Height(), nHeight);
deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip());
deterministicMNManager->DoMaintenance();
auto tip_list = deterministicMNManager->GetListAtChainTip();
BOOST_ASSERT(tip_list.HasMN(tx_reg_hash));
auto pindex_create = ::ChainActive().Tip();
auto base_list = deterministicMNManager->GetListForBlock(pindex_create);
std::vector<CDeterministicMNListDiff> diffs;
// update
CBLSSecretKey operator_key_new;
operator_key_new.MakeNewKey();
auto tx_upreg = CreateProUpRegTx(*(setup.m_node.mempool), utxos, tx_reg_hash, owner_key, operator_key_new.GetPublicKey(), owner_key.GetPubKey().GetID(), collateralScript, setup.coinbaseKey);
block = std::make_shared<CBlock>(setup.CreateBlock({tx_upreg}, setup.coinbaseKey));
BOOST_ASSERT(Assert(setup.m_node.chainman)->ProcessNewBlock(Params(), block, true, nullptr));
BOOST_ASSERT(!llmq::utils::IsV19Active(::ChainActive().Tip()));
++nHeight;
BOOST_CHECK_EQUAL(::ChainActive().Height(), nHeight);
deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip());
deterministicMNManager->DoMaintenance();
tip_list = deterministicMNManager->GetListAtChainTip();
BOOST_ASSERT(tip_list.HasMN(tx_reg_hash));
diffs.push_back(base_list.BuildDiff(tip_list));
// spend
CMutableTransaction tx_spend;
COutPoint collateralOutpoint(tx_reg_hash, 0);
tx_spend.vin.emplace_back(collateralOutpoint);
tx_spend.vout.emplace_back(999.99 * COIN, collateralScript);
FillableSigningProvider signing_provider;
signing_provider.AddKeyPubKey(collateral_key, collateral_key.GetPubKey());
BOOST_ASSERT(SignSignature(signing_provider, CTransaction(tx_reg), tx_spend, 0, SIGHASH_ALL));
block = std::make_shared<CBlock>(setup.CreateBlock({tx_spend}, setup.coinbaseKey));
BOOST_ASSERT(Assert(setup.m_node.chainman)->ProcessNewBlock(Params(), block, true, nullptr));
BOOST_ASSERT(!llmq::utils::IsV19Active(::ChainActive().Tip()));
++nHeight;
BOOST_CHECK_EQUAL(::ChainActive().Height(), nHeight);
deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip());
deterministicMNManager->DoMaintenance();
diffs.push_back(tip_list.BuildDiff(deterministicMNManager->GetListAtChainTip()));
tip_list = deterministicMNManager->GetListAtChainTip();
BOOST_ASSERT(!tip_list.HasMN(tx_reg_hash));
BOOST_ASSERT(deterministicMNManager->GetListForBlock(pindex_create).HasMN(tx_reg_hash));
// mine another block so that it's not the last one before V19
setup.CreateAndProcessBlock({}, setup.coinbaseKey);
BOOST_ASSERT(!llmq::utils::IsV19Active(::ChainActive().Tip()));
++nHeight;
BOOST_CHECK_EQUAL(::ChainActive().Height(), nHeight);
deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip());
deterministicMNManager->DoMaintenance();
diffs.push_back(tip_list.BuildDiff(deterministicMNManager->GetListAtChainTip()));
tip_list = deterministicMNManager->GetListAtChainTip();
BOOST_ASSERT(!tip_list.HasMN(tx_reg_hash));
BOOST_ASSERT(deterministicMNManager->GetListForBlock(pindex_create).HasMN(tx_reg_hash));
// this block should activate V19
setup.CreateAndProcessBlock({}, setup.coinbaseKey);
BOOST_ASSERT(llmq::utils::IsV19Active(::ChainActive().Tip()));
++nHeight;
BOOST_CHECK_EQUAL(::ChainActive().Height(), nHeight);
deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip());
deterministicMNManager->DoMaintenance();
diffs.push_back(tip_list.BuildDiff(deterministicMNManager->GetListAtChainTip()));
tip_list = deterministicMNManager->GetListAtChainTip();
BOOST_ASSERT(!tip_list.HasMN(tx_reg_hash));
BOOST_ASSERT(deterministicMNManager->GetListForBlock(pindex_create).HasMN(tx_reg_hash));
// check mn list/diff
CDeterministicMNListDiff dummy_diff = base_list.BuildDiff(tip_list);
CDeterministicMNList dummmy_list = base_list.ApplyDiff(::ChainActive().Tip(), dummy_diff);
// Lists should match
BOOST_ASSERT(dummmy_list == tip_list);
// mine 10 more blocks
for (int i = 0; i < 10; ++i)
{
setup.CreateAndProcessBlock({}, setup.coinbaseKey);
BOOST_ASSERT(llmq::utils::IsV19Active(::ChainActive().Tip()));
BOOST_CHECK_EQUAL(::ChainActive().Height(), nHeight + 1 + i);
deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip());
deterministicMNManager->DoMaintenance();
diffs.push_back(tip_list.BuildDiff(deterministicMNManager->GetListAtChainTip()));
tip_list = deterministicMNManager->GetListAtChainTip();
BOOST_ASSERT(!tip_list.HasMN(tx_reg_hash));
BOOST_ASSERT(deterministicMNManager->GetListForBlock(pindex_create).HasMN(tx_reg_hash));
}
// check mn list/diff
const CBlockIndex* v19_index = llmq::utils::V19ActivationIndex(::ChainActive().Tip());
auto v19_list = deterministicMNManager->GetListForBlock(v19_index);
dummy_diff = v19_list.BuildDiff(tip_list);
dummmy_list = v19_list.ApplyDiff(::ChainActive().Tip(), dummy_diff);
BOOST_ASSERT(dummmy_list == tip_list);
// NOTE: this fails on v19/v19.1 with errors like:
// "RemoveMN: Can't delete a masternode ... with a pubKeyOperator=..."
dummy_diff = base_list.BuildDiff(tip_list);
dummmy_list = base_list.ApplyDiff(::ChainActive().Tip(), dummy_diff);
BOOST_ASSERT(dummmy_list == tip_list);
dummmy_list = base_list;
for (const auto& diff : diffs) {
dummmy_list = dummmy_list.ApplyDiff(::ChainActive().Tip(), diff);
}
BOOST_ASSERT(dummmy_list == tip_list);
};
void FuncDIP3Protx(TestChainSetup& setup)
{
CKey sporkKey;
@ -481,7 +612,7 @@ void FuncTestMempoolReorg(TestChainSetup& setup)
payload.nVersion = CProRegTx::GetVersion(!bls::bls_legacy_scheme);
payload.addr = LookupNumeric("1.1.1.1", 1);
payload.keyIDOwner = ownerKey.GetPubKey().GetID();
payload.pubKeyOperator = operatorKey.GetPublicKey();
payload.pubKeyOperator.Set(operatorKey.GetPublicKey(), bls::bls_legacy_scheme.load());
payload.keyIDVoting = ownerKey.GetPubKey().GetID();
payload.scriptPayout = scriptPayout;
@ -550,7 +681,7 @@ void FuncTestMempoolDualProregtx(TestChainSetup& setup)
CProRegTx payload;
payload.addr = LookupNumeric("1.1.1.1", 2);
payload.keyIDOwner = ownerKey.GetPubKey().GetID();
payload.pubKeyOperator = operatorKey.GetPublicKey();
payload.pubKeyOperator.Set(operatorKey.GetPublicKey(), bls::bls_legacy_scheme.load());
payload.keyIDVoting = ownerKey.GetPubKey().GetID();
payload.scriptPayout = scriptPayout;
@ -612,7 +743,7 @@ void FuncVerifyDB(TestChainSetup& setup)
payload.nVersion = CProRegTx::GetVersion(!bls::bls_legacy_scheme);
payload.addr = LookupNumeric("1.1.1.1", 1);
payload.keyIDOwner = ownerKey.GetPubKey().GetID();
payload.pubKeyOperator = operatorKey.GetPublicKey();
payload.pubKeyOperator.Set(operatorKey.GetPublicKey(), bls::bls_legacy_scheme.load());
payload.keyIDVoting = ownerKey.GetPubKey().GetID();
payload.scriptPayout = scriptPayout;
@ -667,6 +798,13 @@ BOOST_AUTO_TEST_CASE(dip3_activation_legacy)
FuncDIP3Activation(setup);
}
// V19 can only be activated with legacy scheme
BOOST_AUTO_TEST_CASE(v19_activation_legacy)
{
TestChainV19BeforeActivationSetup setup;
FuncV19Activation(setup);
}
BOOST_AUTO_TEST_CASE(dip3_protx_legacy)
{
TestChainDIP3Setup setup;

View File

@ -28,7 +28,7 @@ BOOST_AUTO_TEST_CASE(simplifiedmns_merkleroots)
std::vector<unsigned char> vecBytes{static_cast<unsigned char>(i)};
vecBytes.resize(CBLSSecretKey::SerSize);
smle.pubKeyOperator.Set(CBLSSecretKey(vecBytes).GetPublicKey());
smle.pubKeyOperator.Set(CBLSSecretKey(vecBytes).GetPublicKey(), bls::bls_legacy_scheme.load());
smle.keyIDVoting.SetHex(strprintf("%040x", i));
smle.isValid = true;

View File

@ -29,14 +29,14 @@ void Test(llmq::CQuorumManager& qman)
BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeDIP0024InstantSend, qman, nullptr, true, false), true);
BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeDIP0024InstantSend, qman, nullptr, true, true), true);
BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeChainLocks, qman, nullptr, false, false), true);
BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeChainLocks, qman, nullptr, false, false), true);
BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeChainLocks, qman, nullptr, true, false), true);
BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeChainLocks, qman, nullptr, true, true), true);
BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypePlatform, qman, nullptr, false, false), Params().IsTestChain());
BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypePlatform, qman, nullptr, true, false), Params().IsTestChain());
BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypePlatform, qman, nullptr, true, true), Params().IsTestChain());
BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypePlatform, qman, nullptr, true, true), Params().IsTestChain());
BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeMnhf, qman, nullptr, false, false), true);
BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeMnhf, qman, nullptr, true, false), true);
BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeMnhf, qman, nullptr, true, true), true);
BOOST_CHECK_EQUAL(IsQuorumTypeEnabledInternal(consensus_params.llmqTypeMnhf, qman, nullptr, true, true), true);
}
BOOST_FIXTURE_TEST_CASE(utils_IsQuorumTypeEnabled_tests_regtest, RegTestingSetup)

View File

@ -24,6 +24,7 @@
#include <llmq/signing_shares.h>
#include <llmq/signing.h>
#include <llmq/snapshot.h>
#include <llmq/utils.h>
#include <miner.h>
#include <net.h>
#include <net_processing.h>
@ -391,3 +392,9 @@ CBlock getBlock13b8a()
stream >> block;
return block;
}
TestChainV19BeforeActivationSetup::TestChainV19BeforeActivationSetup() : TestChainSetup(894)
{
bool v19_active = llmq::utils::IsV19Active(::ChainActive().Tip());
assert(!v19_active);
}

View File

@ -152,6 +152,11 @@ struct TestChainDIP3BeforeActivationSetup : public TestChainSetup
TestChainDIP3BeforeActivationSetup() : TestChainSetup(430) {}
};
struct TestChainV19BeforeActivationSetup : public TestChainSetup
{
TestChainV19BeforeActivationSetup();
};
class CTxMemPoolEntry;
struct TestMemPoolEntryHelper

View File

@ -19,7 +19,6 @@
#include <hash.h>
#include <validationinterface.h>
#include <bls/bls.h>
#include <evo/specialtx.h>
#include <evo/providertx.h>
#include <evo/deterministicmns.h>
@ -443,7 +442,7 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(proTx.proTxHash);
assert(dmn);
newit->validForProTxKey = ::SerializeHash(dmn->pdmnState->pubKeyOperator);
if (dmn->pdmnState->pubKeyOperator.Get() != proTx.pubKeyOperator) {
if (dmn->pdmnState->pubKeyOperator != proTx.pubKeyOperator) {
newit->isKeyChangeProTx = true;
}
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REVOKE) {
@ -838,7 +837,7 @@ void CTxMemPool::removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID
}
}
void CTxMemPool::removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSPublicKey &pubKey)
void CTxMemPool::removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSLazyPublicKey &pubKey)
{
if (mapProTxBlsPubKeyHashes.count(pubKey.GetHash())) {
uint256 conflictHash = mapProTxBlsPubKeyHashes[pubKey.GetHash()];
@ -1301,7 +1300,7 @@ bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const {
return true; // i.e. failed to find validated ProTx == conflict
}
// only allow one operator key change in the mempool
if (dmn->pdmnState->pubKeyOperator.Get() != proTx.pubKeyOperator) {
if (dmn->pdmnState->pubKeyOperator != proTx.pubKeyOperator) {
if (hasKeyChangeInMempool(proTx.proTxHash)) {
return true;
}

View File

@ -17,6 +17,7 @@
#include <addressindex.h>
#include <spentindex.h>
#include <amount.h>
#include <bls/bls.h>
#include <coins.h>
#include <crypto/siphash.h>
#include <indirectmap.h>
@ -33,8 +34,6 @@
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
class CBLSPublicKey;
class CBlockIndex;
extern CCriticalSection cs_main;
@ -616,7 +615,7 @@ public:
void removeForReorg(const CCoinsViewCache* pcoins, unsigned int nMemPoolHeight, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
void removeConflicts(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID &keyId) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSPublicKey &pubKey) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSLazyPublicKey &pubKey) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeProTxCollateralConflicts(const CTransaction &tx, const COutPoint &collateralOutpoint) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeProTxSpentCollateralConflicts(const CTransaction &tx) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeProTxKeyChangedConflicts(const CTransaction &tx, const uint256& proTxHash, const uint256& newKeyHash) EXCLUSIVE_LOCKS_REQUIRED(cs);

View File

@ -36,7 +36,6 @@ public:
template<typename Value2>
void _emplace(const Key& key, Value2&& v)
{
truncate_if_needed();
auto it = cacheMap.find(key);
if (it == cacheMap.end()) {
cacheMap.emplace(key, std::make_pair(std::forward<Value2>(v), accessCounter++));
@ -44,6 +43,7 @@ public:
it->second.first = std::forward<Value2>(v);
it->second.second = accessCounter++;
}
truncate_if_needed();
}
void emplace(const Key& key, Value&& v)

View File

@ -4214,6 +4214,8 @@ bool TestBlockValidity(CValidationState& state, llmq::CChainLocksHandler& clhand
AssertLockHeld(cs_main);
assert(pindexPrev && pindexPrev == ::ChainActive().Tip());
auto bls_legacy_scheme = bls::bls_legacy_scheme.load();
uint256 hash = block.GetHash();
if (clhandler.HasConflictingChainLock(pindexPrev->nHeight + 1, hash)) {
return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_PREV, error("%s: conflicting with chainlock", __func__), REJECT_INVALID, "bad-chainlock");
@ -4240,6 +4242,12 @@ bool TestBlockValidity(CValidationState& state, llmq::CChainLocksHandler& clhand
return false;
assert(state.IsValid());
// we could switch to another scheme while testing, switch back to the original one
if (bls_legacy_scheme != bls::bls_legacy_scheme.load()) {
bls::bls_legacy_scheme.store(bls_legacy_scheme);
LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load());
}
return true;
}

View File

@ -11,7 +11,7 @@
*/
static const int PROTOCOL_VERSION = 70227;
static const int PROTOCOL_VERSION = 70228;
//! initial proto version, to be increased after version/verack negotiation
static const int INIT_PROTO_VERSION = 209;
@ -53,6 +53,9 @@ static const int COINJOIN_PROTX_HASH_PROTO_VERSION = 70226;
//! Masternode type was introduced in this version
static const int DMN_TYPE_PROTO_VERSION = 70227;
//! Versioned Simplified Masternode List Entries were introduced in this version
static const int SMNLE_VERSIONED_PROTO_VERSION = 70228;
// Make sure that none of the values above collide with `ADDRV2_FORMAT`.
#endif // BITCOIN_VERSION_H

View File

@ -620,7 +620,7 @@ UniValue importwallet(const JSONRPCRequest& request)
UniValue importelectrumwallet(const JSONRPCRequest& request)
{
RPCHelpMan{"importselectrumwallet",
RPCHelpMan{"importelectrumwallet",
"\nImports keys from an Electrum wallet export file (.csv or .json)\n",
{
{"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The Electrum wallet export file, should be in csv or json format"},

View File

@ -2864,7 +2864,7 @@ static UniValue createwallet(const JSONRPCRequest& request)
{
{"wallet_name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name for the new wallet. If this is a path, the wallet will be created at the path location."},
{"disable_private_keys", RPCArg::Type::BOOL, /* default */ "false", "Disable the possibility of private keys (only watchonlys are possible in this mode)."},
{"blank", RPCArg::Type::BOOL, /* default */ "false", "Create a blank wallet. A blank wallet has no keys or HD seed. One can be set using sethdseed."},
{"blank", RPCArg::Type::BOOL, /* default */ "false", "Create a blank wallet. A blank wallet has no keys or HD seed. One can be set using upgradetohd."},
{"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Encrypt the wallet with this passphrase."},
{"avoid_reuse", RPCArg::Type::BOOL, /* default */ "false", "Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind."},
{"load_on_startup", RPCArg::Type::BOOL, /* default */ "null", "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},

View File

@ -486,7 +486,9 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
strErr = "Found unsupported 'wkey' record, try loading with version 0.17";
return false;
} else if (strType != DBKeys::BESTBLOCK && strType != DBKeys::BESTBLOCK_NOMERKLE &&
strType != DBKeys::MINVERSION && strType != DBKeys::ACENTRY && strType != DBKeys::VERSION) {
strType != DBKeys::MINVERSION && strType != DBKeys::ACENTRY &&
strType != DBKeys::VERSION &&
strType != DBKeys::PRIVATESEND_SALT && strType != DBKeys::COINJOIN_SALT) {
wss.m_unknown_records++;
}
} catch (const std::exception& e) {

View File

@ -66,9 +66,18 @@ class DIP3V19Test(DashTestFramework):
b_0 = self.nodes[0].getbestblockhash()
self.test_getmnlistdiff(null_hash, b_0, {}, [], expected_updated)
mn_list_before = self.nodes[0].masternodelist()
pubkeyoperator_list_before = set([mn_list_before[e]["pubkeyoperator"] for e in mn_list_before])
self.activate_v19(expected_activation_height=900)
self.log.info("Activated v19 at height:" + str(self.nodes[0].getblockcount()))
mn_list_after = self.nodes[0].masternodelist()
pubkeyoperator_list_after = set([mn_list_after[e]["pubkeyoperator"] for e in mn_list_after])
self.log.info("pubkeyoperator should still be shown using legacy scheme")
assert_equal(pubkeyoperator_list_before, pubkeyoperator_list_after)
self.move_to_next_cycle()
self.log.info("Cycle H height:" + str(self.nodes[0].getblockcount()))
self.move_to_next_cycle()
@ -101,8 +110,22 @@ class DIP3V19Test(DashTestFramework):
self.test_revoke_protx(revoke_protx, revoke_keyoperator)
self.mine_quorum(llmq_type_name='llmq_test', llmq_type=100)
# revoking a MN results in disconnects, reconnect it back to let sync_blocks finish correctly
self.connect_nodes(hpmn_info_3.nodeIdx, 0)
return
self.log.info("Checking that adding more regular MNs after v19 doesn't break DKGs and IS/CLs")
for i in range(6):
new_mn = self.dynamically_add_masternode(hpmn=False, rnd=(10 + i))
assert new_mn is not None
# mine more quorums and make sure everything still works
prev_quorum = None
for _ in range(5):
quorum = self.mine_quorum()
assert prev_quorum != quorum
self.wait_for_chainlocked_block_all_nodes(self.nodes[0].getbestblockhash())
def test_revoke_protx(self, revoke_protx, revoke_keyoperator):
funds_address = self.nodes[0].getnewaddress()
@ -143,7 +166,7 @@ class DIP3V19Test(DashTestFramework):
# Verify that the merkle root matches what we locally calculate
hashes = []
for mn in sorted(new_mn_list.values(), key=lambda mn: ser_uint256(mn.proRegTxHash)):
hashes.append(hash256(mn.serialize()))
hashes.append(hash256(mn.serialize(with_version = False)))
merkle_root = CBlock.get_merkle_root(hashes)
assert_equal(merkle_root, cbtx.merkleRootMNList)

View File

@ -181,7 +181,7 @@ class LLMQCoinbaseCommitmentsTest(DashTestFramework):
# Verify that the merkle root matches what we locally calculate
hashes = []
for mn in sorted(newMNList.values(), key=lambda mn: ser_uint256(mn.proRegTxHash)):
hashes.append(hash256(mn.serialize()))
hashes.append(hash256(mn.serialize(with_version = False)))
merkleRoot = CBlock.get_merkle_root(hashes)
assert_equal(merkleRoot, cbtx.merkleRootMNList)

View File

@ -52,6 +52,20 @@ class LLMQChainLocksTest(DashTestFramework):
block = self.nodes[0].getblock(self.nodes[0].getblockhash(h))
assert block['chainlock']
# Update spork to SPORK_19_CHAINLOCKS_ENABLED and test its behaviour
self.nodes[0].sporkupdate("SPORK_19_CHAINLOCKS_ENABLED", 1)
self.wait_for_sporks_same()
# Generate new blocks and verify that they are not chainlocked
previous_block_hash = self.nodes[0].getbestblockhash()
for _ in range(2):
block_hash = self.nodes[0].generate(1)[0]
self.wait_for_chainlocked_block_all_nodes(block_hash, expected=False)
assert self.nodes[0].getblock(previous_block_hash)["chainlock"]
self.nodes[0].sporkupdate("SPORK_19_CHAINLOCKS_ENABLED", 0)
self.wait_for_sporks_same()
self.log.info("Isolate node, mine on another, and reconnect")
self.isolate_node(0)
node0_mining_addr = self.nodes[0].getnewaddress()

View File

@ -253,7 +253,7 @@ class LLMQHPMNTest(DashTestFramework):
# Verify that the merkle root matches what we locally calculate
hashes = []
for mn in sorted(newMNList.values(), key=lambda mn: ser_uint256(mn.proRegTxHash)):
hashes.append(hash256(mn.serialize()))
hashes.append(hash256(mn.serialize(with_version = False)))
merkleRoot = CBlock.get_merkle_root(hashes)
assert_equal(merkleRoot, cbtx.merkleRootMNList)

View File

@ -32,7 +32,7 @@ from test_framework.util import hex_str_to_bytes, assert_equal
import dash_hash
MIN_VERSION_SUPPORTED = 60001
MY_VERSION = 70227 # DMN_TYPE_PROTO_VERSION
MY_VERSION = 70228 # SMNLE_VERSIONED_PROTO_VERSION
MY_SUBVERSION = b"/python-mininode-tester:0.0.3%s/"
MY_RELAY = 1 # from version 70001 onwards, fRelay should be appended to version messages (BIP37)
@ -1078,7 +1078,7 @@ class CCbTx:
class CSimplifiedMNListEntry:
__slots__ = ("proRegTxHash", "confirmedHash", "service", "pubKeyOperator", "keyIDVoting", "isValid", "version", "type", "platformHTTPPort", "platformNodeID")
__slots__ = ("proRegTxHash", "confirmedHash", "service", "pubKeyOperator", "keyIDVoting", "isValid", "nVersion", "type", "platformHTTPPort", "platformNodeID")
def __init__(self):
self.set_null()
@ -1090,34 +1090,36 @@ class CSimplifiedMNListEntry:
self.pubKeyOperator = b'\x00' * 48
self.keyIDVoting = 0
self.isValid = False
self.version = 0
self.nVersion = 0
self.type = 0
self.platformHTTPPort = 0
self.platformNodeID = b'\x00' * 20
def deserialize(self, f, version):
self.version = version # memory only
def deserialize(self, f):
self.nVersion = struct.unpack("<H", f.read(2))[0]
self.proRegTxHash = deser_uint256(f)
self.confirmedHash = deser_uint256(f)
self.service.deserialize(f)
self.pubKeyOperator = f.read(48)
self.keyIDVoting = f.read(20)
self.isValid = struct.unpack("<?", f.read(1))[0]
if self.version == 2:
if self.nVersion == 2:
self.type = struct.unpack("<H", f.read(2))[0]
if self.type == 1:
self.platformHTTPPort = struct.unpack("<H", f.read(2))[0]
self.platformNodeID = f.read(20)
def serialize(self):
def serialize(self, with_version = True):
r = b""
if with_version:
r += struct.pack("<H", self.nVersion)
r += ser_uint256(self.proRegTxHash)
r += ser_uint256(self.confirmedHash)
r += self.service.serialize()
r += self.pubKeyOperator
r += self.keyIDVoting
r += struct.pack("<?", self.isValid)
if self.version == 2:
if self.nVersion == 2:
r += struct.pack("<H", self.type)
if self.type == 1:
r += struct.pack("<H", self.platformHTTPPort)
@ -1920,7 +1922,7 @@ class msg_getmnlistd:
QuorumId = namedtuple('QuorumId', ['llmqType', 'quorumHash'])
class msg_mnlistdiff:
__slots__ = ("baseBlockHash", "blockHash", "merkleProof", "cbTx", "version", "deletedMNs", "mnList", "deletedQuorums", "newQuorums",)
__slots__ = ("baseBlockHash", "blockHash", "merkleProof", "cbTx", "nVersion", "deletedMNs", "mnList", "deletedQuorums", "newQuorums",)
command = b"mnlistdiff"
def __init__(self):
@ -1928,7 +1930,7 @@ class msg_mnlistdiff:
self.blockHash = 0
self.merkleProof = CPartialMerkleTree()
self.cbTx = None
self.version = 0
self.nVersion = 0
self.deletedMNs = []
self.mnList = []
self.deletedQuorums = []
@ -1941,12 +1943,12 @@ class msg_mnlistdiff:
self.cbTx = CTransaction()
self.cbTx.deserialize(f)
self.cbTx.rehash()
self.version = struct.unpack("<H", f.read(2))[0]
self.nVersion = struct.unpack("<H", f.read(2))[0]
self.deletedMNs = deser_uint256_vector(f)
self.mnList = []
for i in range(deser_compact_size(f)):
e = CSimplifiedMNListEntry()
e.deserialize(f, self.version)
e.deserialize(f)
self.mnList.append(e)
self.deletedQuorums = []

View File

@ -1067,7 +1067,7 @@ class DashTestFramework(BitcoinTestFramework):
created_mn_info = self.dynamically_prepare_masternode(mn_idx, node_p2p_port, hpmn, rnd)
protx_success = True
except:
self.log.info("protx_hpmn rejected")
self.log.info("dynamically_prepare_masternode failed")
assert_equal(protx_success, not should_be_rejected)
@ -1083,7 +1083,7 @@ class DashTestFramework(BitcoinTestFramework):
for mn_info in self.mninfo:
if mn_info.proTxHash == created_mn_info.proTxHash:
mn_info.nodeIx = mn_idx
mn_info.nodeIdx = mn_idx
mn_info.node = self.nodes[mn_idx]
self.connect_nodes(mn_idx, 0)
@ -1104,8 +1104,8 @@ class DashTestFramework(BitcoinTestFramework):
reward_address = self.nodes[0].getnewaddress()
platform_node_id = hash160(b'%d' % rnd).hex() if rnd is not None else hash160(b'%d' % node_p2p_port).hex()
platform_p2p_port = '%d' % (node_p2p_port + 101) if hpmn else ''
platform_http_port = '%d' % (node_p2p_port + 102) if hpmn else ''
platform_p2p_port = '%d' % (node_p2p_port + 101)
platform_http_port = '%d' % (node_p2p_port + 102)
collateral_amount = 4000 if hpmn else 1000
outputs = {collateral_address: collateral_amount, funds_address: 1}
@ -1126,8 +1126,12 @@ class DashTestFramework(BitcoinTestFramework):
ipAndPort = '127.0.0.1:%d' % node_p2p_port
operatorReward = idx
register_rpc = 'register_hpmn' if hpmn else 'register'
protx_result = self.nodes[0].protx(register_rpc, collateral_txid, collateral_vout, ipAndPort, owner_address, bls['public'], voting_address, operatorReward, reward_address, platform_node_id, platform_p2p_port, platform_http_port, funds_address, True)
protx_result = None
if hpmn:
protx_result = self.nodes[0].protx("register_hpmn", collateral_txid, collateral_vout, ipAndPort, owner_address, bls['public'], voting_address, operatorReward, reward_address, platform_node_id, platform_p2p_port, platform_http_port, funds_address, True)
else:
protx_result = self.nodes[0].protx("register", collateral_txid, collateral_vout, ipAndPort, owner_address, bls['public'], voting_address, operatorReward, reward_address, funds_address, True)
self.wait_for_instantlock(protx_result, self.nodes[0])
tip = self.nodes[0].generate(1)[0]
self.sync_all(self.nodes)
@ -1467,9 +1471,9 @@ class DashTestFramework(BitcoinTestFramework):
if wait_until(check_chainlocked_block, timeout=timeout, sleep=0.1, do_assert=expected) and not expected:
raise AssertionError("waiting unexpectedly succeeded")
def wait_for_chainlocked_block_all_nodes(self, block_hash, timeout=15):
def wait_for_chainlocked_block_all_nodes(self, block_hash, timeout=15, expected=True):
for node in self.nodes:
self.wait_for_chainlocked_block(node, block_hash, timeout=timeout)
self.wait_for_chainlocked_block(node, block_hash, expected=expected, timeout=timeout)
def wait_for_best_chainlock(self, node, block_hash, timeout=15):
wait_until(lambda: node.getbestchainlock()["blockhash"] == block_hash, timeout=timeout, sleep=0.1)