From 3275953ebf337c86383bdc10f823ae0b91b5e7e8 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Wed, 24 May 2023 19:39:17 +0300 Subject: [PATCH 01/20] docs: actually add release-notes-19.0.0.md (#5389) ## Issue being fixed or feature implemented we missed it in https://github.com/dashpay/dash/pull/5385 ## What was done? ## How Has This Been Tested? ## Breaking Changes n/a ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --- .../dash/release-notes-19.0.0.md | 289 ++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 doc/release-notes/dash/release-notes-19.0.0.md diff --git a/doc/release-notes/dash/release-notes-19.0.0.md b/doc/release-notes/dash/release-notes-19.0.0.md new file mode 100644 index 0000000000..18d050f270 --- /dev/null +++ b/doc/release-notes/dash/release-notes-19.0.0.md @@ -0,0 +1,289 @@ +# Dash Core version v19.0.0 + +Release is now available from: + + + +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: + + + + +# 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=) to a stream (-printtoconsole) or to a file +(-debuglogfile=). + +- 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 ` 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 Dash’s product needs, such as SegWit and RBF, are excluded from our backporting. For additional detail on what’s 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 From 4510d2b4b03c62ffcb62ccfa1ca869d5604c3264 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Fri, 26 May 2023 21:41:15 +0300 Subject: [PATCH 02/20] build: fix gmp detection on macos when building with no depends (#5394) ## Issue being fixed or feature implemented gmp can't be detected on macos when installed via `brew` atm ## What was done? detect package prefix and adjust CPPFLAGS and LDFLAGS accordingly ## How Has This Been Tested? `./configure` before: `configure: error: libgmp headers missing` after: passes ## Breaking Changes n/a ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --- configure.ac | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/configure.ac b/configure.ac index 8a32cb3719..3092793fd7 100644 --- a/configure.ac +++ b/configure.ac @@ -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. From a1ff8dcfdd1506c582db1a242e470943e6ae1e96 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Wed, 31 May 2023 18:50:17 +0300 Subject: [PATCH 03/20] feat: calculate DISK_SNAPSHOTS at compile time (#5395) ## Issue being fixed or feature implemented LLMQ_400_85 requires four days worth of LLMQs https://github.com/dashpay/dash/blob/master/src/llmq/params.h#L416 ## What was done? ## How Has This Been Tested? n/a ## Breaking Changes n/a ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --- src/evo/deterministicmns.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 899435c941..6e3f03aa3e 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -534,10 +534,20 @@ public: }; +constexpr int llmq_max_blocks() { + int max_blocks{0}; + for (const auto& llmq : Consensus::available_llmqs) { + int blocks = llmq.useRotation ? llmq.dkgInterval * 4 : llmq.dkgInterval * llmq.signingActiveQuorumCount; + 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: From 843206348fe7b6ea20546a0f0b0cd0888f87cfbc Mon Sep 17 00:00:00 2001 From: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com> Date: Wed, 31 May 2023 15:34:14 -0500 Subject: [PATCH 04/20] feat: ability to disable clsig creation while retaining clsig enforcement (#5398) ## Issue being fixed or feature implemented Currently, Chainlocks are either enabled or disabled. This PR adds a third state: enabled but we will not sign new ones. Should probably backport this to v19.x ## What was done? Spork state != 0 but active will now result in chain locks being enforced but not created. ## How Has This Been Tested? ## Breaking Changes None ## Checklist: _Go over all the following points, and put an `x` in all the boxes that apply._ - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --------- Co-authored-by: UdjinM6 --- src/llmq/chainlocks.cpp | 9 +++++++++ src/llmq/chainlocks.h | 1 + test/functional/feature_llmq_chainlocks.py | 14 ++++++++++++++ test/functional/test_framework/test_framework.py | 4 ++-- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/llmq/chainlocks.cpp b/src/llmq/chainlocks.cpp index b33311aeb7..6aee542cdc 100644 --- a/src/llmq/chainlocks.cpp +++ b/src/llmq/chainlocks.cpp @@ -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 diff --git a/src/llmq/chainlocks.h b/src/llmq/chainlocks.h index a96dc01f22..236a113e7e 100644 --- a/src/llmq/chainlocks.h +++ b/src/llmq/chainlocks.h @@ -119,6 +119,7 @@ private: extern std::unique_ptr chainLocksHandler; bool AreChainLocksEnabled(const CSporkManager& sporkManager); +bool ChainLocksSigningEnabled(const CSporkManager& sporkManager); } // namespace llmq diff --git a/test/functional/feature_llmq_chainlocks.py b/test/functional/feature_llmq_chainlocks.py index 87ea496af2..2b30131d09 100755 --- a/test/functional/feature_llmq_chainlocks.py +++ b/test/functional/feature_llmq_chainlocks.py @@ -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() diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 08b3615933..a4a881832d 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -1467,9 +1467,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) From d13e9de34a4c73b77dc18bafda2c1073ad1cbf5e Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Wed, 31 May 2023 23:34:30 +0300 Subject: [PATCH 05/20] test: Fix dynamically_smth_masternode helpers, extend feature_dip3_v19.py (#5402) ## Issue being fixed or feature implemented fix a couple of issues in helpers, extend feature_dip3_v19.py to check more after v19 fork ## What was done? pls see individual PRs ## How Has This Been Tested? run tests ## Breaking Changes n/a ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --- test/functional/feature_dip3_v19.py | 16 +++++++++++++++- test/functional/test_framework/test_framework.py | 16 ++++++++++------ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/test/functional/feature_dip3_v19.py b/test/functional/feature_dip3_v19.py index 87a175610f..855bb76451 100755 --- a/test/functional/feature_dip3_v19.py +++ b/test/functional/feature_dip3_v19.py @@ -101,8 +101,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() diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index a4a881832d..d25e358bde 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -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) From 78bf7221ca204f7b9a7e84d37fb5bb12182f3287 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Sun, 11 Jun 2023 08:41:02 +0300 Subject: [PATCH 06/20] feat: use m_protxHash instead of masternodeOutpoint for hashing dsq and dstx after v19 activation (#5404) ## Issue being fixed or feature implemented Should fix #5401 with minimal potential coinjoin service interruption (~1 minute around v19 fork point) for up to date clients. Fully backwards compatible prior to v19 activation. Old clients won't be able to mix after v19 activation though until they implement similar changes. _EDIT: Actually, this is already the case cause bls sigs are going to change too._ And I think we should also be able to finally drop `masternodeOutpoint` from `CCoinJoinQueue` and `CCoinJoinBroadcastTx` once v19 is active because of that which would be a nice bonus. cc @HashEngineering ## What was done? re-use v19 activation to switch `GetSignatureHash` logic ## How Has This Been Tested? mixing on mainnet ## Breaking Changes mixing won't work on current testnet until MNs are updated ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --- src/coinjoin/coinjoin.cpp | 25 ++++++++++++++----------- src/coinjoin/coinjoin.h | 8 ++++---- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/coinjoin/coinjoin.cpp b/src/coinjoin/coinjoin.cpp index 92acfa664c..5a27efcca8 100644 --- a/src/coinjoin/coinjoin.cpp +++ b/src/coinjoin/coinjoin.cpp @@ -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; } diff --git a/src/coinjoin/coinjoin.h b/src/coinjoin/coinjoin.h index facadb9e2c..0d102f3b87 100644 --- a/src/coinjoin/coinjoin.h +++ b/src/coinjoin/coinjoin.h @@ -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; From 5160db89bf5938f75f026d6e155428bda9b62e00 Mon Sep 17 00:00:00 2001 From: Nathan Marley Date: Wed, 31 May 2023 13:07:46 -0300 Subject: [PATCH 07/20] trivial: Fix typo in import electrum wallet help (#5406) Removes extra 's' after import --- src/wallet/rpcdump.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 732fbfb962..30726dd95a 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -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"}, From ced5d5cbed3a0267b267d1fc527d32c1b862df76 Mon Sep 17 00:00:00 2001 From: thephez Date: Wed, 31 May 2023 12:57:38 -0400 Subject: [PATCH 08/20] docs: correct createwallet help (#5407) ## Issue being fixed or feature implemented Dash does not have `sethdseed`, but the help mentioned it. ## What was done? Switched to `upgradetohd`. ## How Has This Been Tested? ## Breaking Changes N/A ## Checklist: _Go over all the following points, and put an `x` in all the boxes that apply._ - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --- src/wallet/rpcwallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 3e1388da25..992963f47c 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -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."}, From 76a96c29edfc5f7d900c24366087da60684f6ed3 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Sun, 11 Jun 2023 09:57:30 +0300 Subject: [PATCH 09/20] Merge pull request #5410 from UdjinM6/fix_dmnl_cache_issues fix: resolve two DMNL cache issues --- src/evo/deterministicmns.cpp | 14 +++++++------- src/evo/deterministicmns.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index f91214ccea..1116e3d0e4 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -1228,9 +1228,14 @@ void CDeterministicMNManager::CleanupCache(int nHeight) std::vector 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 +1244,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); diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 6e3f03aa3e..e77b05c609 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -537,7 +537,7 @@ public: constexpr int llmq_max_blocks() { int max_blocks{0}; for (const auto& llmq : Consensus::available_llmqs) { - int blocks = llmq.useRotation ? llmq.dkgInterval * 4 : llmq.dkgInterval * llmq.signingActiveQuorumCount; + int blocks = (llmq.useRotation ? 1 : llmq.signingActiveQuorumCount) * llmq.dkgInterval; max_blocks = std::max(max_blocks, blocks); } return max_blocks; From 35ccc8500c6db31875ad26b8288d282ea4bd3d3e Mon Sep 17 00:00:00 2001 From: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com> Date: Tue, 6 Jun 2023 10:56:53 -0500 Subject: [PATCH 10/20] Merge pull request #5415 from kittywhiskers/bp_26633 backport: bitcoin#26633 (update qt 5.12 url to archive location) --- depends/packages/qt.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index 83b9161ad5..d5eae66728 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -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 From e0175b28d7646e6ffdaa4af81419778cc90fe238 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Sun, 4 Jun 2023 23:45:56 +0300 Subject: [PATCH 11/20] fix: Resolve mainnet v19 fork issues (#5403) same as #5392, alternative solution ~based on #5402 atm, will rebase later~ pls see individual commits reorg mainnet around forkpoint with a patched client (to allow low difficulty), run tests Another evodb migration is required. Going back to an older version or migrating after the fork requires reindexing. - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --- src/bls/bls.h | 33 ++-- src/evo/deterministicmns.cpp | 193 +++++++++++++------ src/evo/deterministicmns.h | 53 +++-- src/evo/dmnstate.cpp | 4 +- src/evo/dmnstate.h | 96 ++++++++- src/evo/evodb.h | 3 +- src/evo/providertx.cpp | 8 +- src/evo/providertx.h | 12 +- src/evo/specialtxman.cpp | 19 ++ src/governance/object.cpp | 2 +- src/init.cpp | 22 ++- src/llmq/blockprocessor.cpp | 6 - src/llmq/signing.h | 2 +- src/llmq/signing_shares.cpp | 2 +- src/masternode/node.cpp | 1 + src/masternode/node.h | 1 + src/rpc/evo.cpp | 7 +- src/rpc/governance.cpp | 2 +- src/rpc/masternode.cpp | 6 +- src/rpc/quorums.cpp | 2 +- src/test/block_reward_reallocation_tests.cpp | 2 +- src/test/evo_deterministicmns_tests.cpp | 148 +++++++++++++- src/test/evo_simplifiedmns_tests.cpp | 2 +- src/test/util/setup_common.cpp | 7 + src/test/util/setup_common.h | 5 + src/txmempool.cpp | 7 +- src/txmempool.h | 5 +- src/validation.cpp | 8 + test/functional/feature_dip3_v19.py | 9 + 29 files changed, 519 insertions(+), 148 deletions(-) diff --git a/src/bls/bls.h b/src/bls/bls.h index b160df722a..840b81d8a9 100644 --- a/src/bls/bls.h +++ b/src/bls/bls.h @@ -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) { @@ -459,12 +456,8 @@ public: { std::unique_lock l(mutex); if (!objInitialized && !bufValid) { - // the all-zero buf is considered a valid buf 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 +469,7 @@ public: template inline void Serialize(Stream& s) const { - Serialize(s, bls::bls_legacy_scheme.load()); + Serialize(s, bufLegacyScheme); } template @@ -493,13 +486,14 @@ public: template 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 l(mutex); bufValid = false; + bufLegacyScheme = specificLegacyScheme; objInitialized = true; obj = _obj; hash.SetNull(); @@ -549,13 +543,15 @@ public: return !(*this == r); } - uint256 GetHash(const bool specificLegacyScheme = bls::bls_legacy_scheme.load()) const + uint256 GetHash() const { std::unique_lock l(mutex); - if (!bufValid || bufLegacyScheme != specificLegacyScheme) { - vecBytes = obj.ToByteVector(specificLegacyScheme); + if (!objInitialized && !bufValid) { + 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 +561,11 @@ public: } return hash; } + + std::string ToString() const + { + return Get().ToString(bufLegacyScheme); + } }; using CBLSLazySignature = CBLSLazyWrapper; using CBLSLazyPublicKey = CBLSLazyWrapper; diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 1116e3d0e4..0b243ed0b2 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -25,8 +25,8 @@ #include -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 deterministicMNManager; @@ -463,48 +463,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 +496,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) { @@ -580,7 +538,7 @@ 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)) { @@ -639,7 +597,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,17 +645,14 @@ 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); + // NOTE: The block next to the activation is the one that is using new rules. + // If the fork was activated at pindex->prev block then current one is the first one + // using new rules. Save mn list snapsot for this block. + bool v19_just_activated = pindex->pprev == llmq::utils::V19ActivationIndex(pindex); + m_evoDb.Write(std::make_pair(DB_LIST_DIFF, newList.GetBlockHash()), diff); if ((nHeight % DISK_SNAPSHOT_PERIOD) == 0 || oldList.GetHeight() == -1 || v19_just_activated) { m_evoDb.Write(std::make_pair(DB_LIST_SNAPSHOT, newList.GetBlockHash()), newList); @@ -940,12 +895,12 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-hash"); } auto newState = std::make_shared(*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); } - newState->pubKeyOperator.Set(proTx.pubKeyOperator); + newState->pubKeyOperator = proTx.pubKeyOperator; newState->keyIDVoting = proTx.keyIDVoting; newState->scriptPayout = proTx.scriptPayout; @@ -1266,6 +1221,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 +1233,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 +1266,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 +1282,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 +1291,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 +1307,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; } diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index e77b05c609..d02fdd51c0 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -43,8 +43,10 @@ private: uint64_t internalId{std::numeric_limits::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(old_state); + } else if (ser_action.ForRead() && format_version == MN_TYPE_FORMAT) { + CDeterministicMNState_mntype_format old_state; + READWRITE(old_state); + pdmnState = std::make_shared(old_state); } else { READWRITE(pdmnState); } @@ -98,11 +104,11 @@ public: template void Serialize(Stream& s) const { - const_cast(this)->SerializationOp(s, CSerActionSerialize(), MN_TYPE_FORMAT); + const_cast(this)->SerializationOp(s, CSerActionSerialize(), MN_CURRENT_FORMAT); } template - 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 - 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& pdmnState); void UpdateMN(const uint256& proTxHash, const std::shared_ptr& pdmnState); @@ -396,12 +400,12 @@ public: template [[nodiscard]] bool HasUniqueProperty(const T& v) const { - return mnUniquePropertyMap.count(::SerializeHash(v)) != 0; + return mnUniquePropertyMap.count(GetUniquePropertyHash(v)) != 0; } template [[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 + [[nodiscard]] uint256 GetUniquePropertyHash(const T& v) const + { + if constexpr (std::is_same()) { + assert(false); + } + return ::SerializeHash(v); + } template [[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 - 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(mn); addedMNs.push_back(dmn); @@ -594,6 +614,7 @@ public: bool IsDIP3Enforced(int nHeight = -1); bool MigrateDBIfNeeded(); + bool MigrateDBIfNeeded2(); void DoMaintenance(); diff --git a/src/evo/dmnstate.cpp b/src/evo/dmnstate.cpp index 7cbdf373aa..d2be5cefbf 100644 --- a/src/evo/dmnstate.cpp +++ b/src/evo/dmnstate.cpp @@ -29,7 +29,7 @@ std::string CDeterministicMNState::ToString() const return strprintf("CDeterministicMNState(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); + EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.ToString(), EncodeDestination(PKHash(keyIDVoting)), addr.ToStringIPPort(false), payoutAddress, operatorPayoutAddress); } void CDeterministicMNState::ToJson(UniValue& obj, MnType nType) const @@ -56,7 +56,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)); } diff --git a/src/evo/dmnstate.h b/src/evo/dmnstate.h index 9f3aa52084..fb8e17c040 100644 --- a/src/evo/dmnstate.h +++ b/src/evo/dmnstate.h @@ -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 CDeterministicMNState(deserialize_type, Stream& s) { @@ -141,6 +221,7 @@ public: SERIALIZE_METHODS(CDeterministicMNState, obj) { READWRITE( + obj.nVersion, obj.nRegisteredHeight, obj.nLastPaidHeight, obj.nConsecutivePayments, @@ -151,7 +232,9 @@ public: obj.confirmedHash, obj.confirmedHashWithProRegTxHash, obj.keyIDOwner, - obj.pubKeyOperator, + obj.keyIDOwner); + READWRITE(CBLSLazyPublicKeyVersionWrapper(const_cast(obj.pubKeyOperator), obj.nVersion == CProRegTx::LEGACY_BLS_VERSION)); + READWRITE( obj.keyIDVoting, obj.addr, obj.scriptPayout, @@ -163,7 +246,7 @@ public: void ResetOperatorFields() { - pubKeyOperator.Set(CBLSPublicKey()); + pubKeyOperator.Set(CBLSPublicKey(), bls::bls_legacy_scheme.load()); 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}; @@ -266,8 +351,7 @@ public: 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(obj.state.pubKeyOperator), true)); \ + READWRITE(CBLSLazyPublicKeyVersionWrapper(const_cast(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 diff --git a/src/evo/evodb.h b/src/evo/evodb.h index 9334603f24..9734711762 100644 --- a/src/evo/evodb.h +++ b/src/evo/evodb.h @@ -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; diff --git a/src/evo/providertx.cpp b/src/evo/providertx.cpp index 26123cb63e..d7d3ba8b0f 100644 --- a/src/evo/providertx.cpp +++ b/src/evo/providertx.cpp @@ -23,7 +23,7 @@ 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 (!scriptPayout.IsPayToPublicKeyHash() && !scriptPayout.IsPayToScriptHash()) { @@ -81,7 +81,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,7 +114,7 @@ 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 (!scriptPayout.IsPayToPublicKeyHash() && !scriptPayout.IsPayToScriptHash()) { @@ -132,7 +132,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 diff --git a/src/evo/providertx.h b/src/evo/providertx.h index e113911eab..96d9e8ed89 100644 --- a/src/evo/providertx.h +++ b/src/evo/providertx.h @@ -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(obj.pubKeyOperator), (obj.nVersion == LEGACY_BLS_VERSION)), + CBLSLazyPublicKeyVersionWrapper(const_cast(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(obj.pubKeyOperator), (obj.nVersion == LEGACY_BLS_VERSION)), + CBLSLazyPublicKeyVersionWrapper(const_cast(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()); } diff --git a/src/evo/specialtxman.cpp b/src/evo/specialtxman.cpp index 7a85c790bb..e4afa21314 100644 --- a/src/evo/specialtxman.cpp +++ b/src/evo/specialtxman.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -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::V19ActivationIndex(pindex) == pindex) { + // 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::V19ActivationIndex(pindex) == pindex) { + // 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()); } diff --git a/src/governance/object.cpp b/src/governance/object.cpp index 6d9f4f6150..1e5b936779 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -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; } diff --git a/src/init.cpp b/src/init.cpp index 1fccb986dd..3d4a436ca7 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1702,7 +1702,10 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc activeMasternodeInfo.blsKeyOperator = std::make_unique(keyOperator); activeMasternodeInfo.blsPubKeyOperator = std::make_unique(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(); @@ -2168,6 +2171,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 +2199,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 +2217,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) diff --git a/src/llmq/blockprocessor.cpp b/src/llmq/blockprocessor.cpp index 323884fb77..25a354a4fd 100644 --- a/src/llmq/blockprocessor.cpp +++ b/src/llmq/blockprocessor.cpp @@ -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 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 qcs; diff --git a/src/llmq/signing.h b/src/llmq/signing.h index 1d43916691..ec9d40b04a 100644 --- a/src/llmq/signing.h +++ b/src/llmq/signing.h @@ -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(sig).Set(_sig); UpdateHash();}; + CSigBase(_llmqType, _quorumHash, _id, _msgHash) {const_cast(sig).Set(_sig, bls::bls_legacy_scheme.load()); UpdateHash();}; private: // only in-memory diff --git a/src/llmq/signing_shares.cpp b/src/llmq/signing_shares.cpp index d554db4601..69c576f16a 100644 --- a/src/llmq/signing_shares.cpp +++ b/src/llmq/signing_shares.cpp @@ -1514,7 +1514,7 @@ std::optional 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()); diff --git a/src/masternode/node.cpp b/src/masternode/node.cpp index 3fa6bfc5b1..c398d737e1 100644 --- a/src/masternode/node.cpp +++ b/src/masternode/node.cpp @@ -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; } diff --git a/src/masternode/node.h b/src/masternode/node.h index 9711a933eb..5c0c783eff 100644 --- a/src/masternode/node.h +++ b/src/masternode/node.h @@ -28,6 +28,7 @@ struct CActiveMasternodeInfo { uint256 proTxHash; COutPoint outpoint; CService service; + bool legacy{true}; }; diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index 05c70a4e79..5695a42488 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -661,7 +661,7 @@ 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", ptx.nVersion == CProRegTx::LEGACY_BLS_VERSION), ptx.nVersion == CProRegTx::LEGACY_BLS_VERSION); CKeyID keyIDVoting = ptx.keyIDOwner; if (request.params[paramIdx + 3].get_str() != "") { @@ -703,7 +703,6 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, paramIdx += 3; } - ptx.pubKeyOperator = pubKeyOperator; ptx.keyIDVoting = keyIDVoting; ptx.scriptPayout = GetScriptForDestination(payoutDest); @@ -1041,12 +1040,12 @@ static UniValue protx_update_registrar_wrapper(const JSONRPCRequest& request, co if (!dmn) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("masternode %s not found", ptx.proTxHash.ToString())); } - ptx.pubKeyOperator = dmn->pdmnState->pubKeyOperator.Get(); + ptx.pubKeyOperator = dmn->pdmnState->pubKeyOperator; ptx.keyIDVoting = dmn->pdmnState->keyIDVoting; ptx.scriptPayout = dmn->pdmnState->scriptPayout; if (request.params[1].get_str() != "") { - ptx.pubKeyOperator = ParseBLSPubKey(request.params[1].get_str(), "operator BLS address", specific_legacy_bls_scheme); + ptx.pubKeyOperator.Set(ParseBLSPubKey(request.params[1].get_str(), "operator BLS address", ptx.nVersion == CProUpRegTx::LEGACY_BLS_VERSION), ptx.nVersion == CProRegTx::LEGACY_BLS_VERSION); } if (request.params[2].get_str() != "") { ptx.keyIDVoting = ParsePubKeyIDFromAddress(request.params[2].get_str(), "voting address"); diff --git a/src/rpc/governance.cpp b/src/rpc/governance.cpp index c487c225fa..e1bfdcf996 100644 --- a/src/rpc/governance.cpp +++ b/src/rpc/governance.cpp @@ -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 diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index 51bd842770..1e3d388bca 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -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 && diff --git a/src/rpc/quorums.cpp b/src/rpc/quorums.cpp index a0a0839e0d..62be2aa8e8 100644 --- a/src/rpc/quorums.cpp +++ b/src/rpc/quorums.cpp @@ -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); diff --git a/src/test/block_reward_reallocation_tests.cpp b/src/test/block_reward_reallocation_tests.cpp index 8c457d7923..0a649c5b4d 100644 --- a/src/test/block_reward_reallocation_tests.cpp +++ b/src/test/block_reward_reallocation_tests.cpp @@ -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; diff --git a/src/test/evo_deterministicmns_tests.cpp b/src/test/evo_deterministicmns_tests.cpp index 20528bc787..6fdcb2bce6 100644 --- a/src/test/evo_deterministicmns_tests.cpp +++ b/src/test/evo_deterministicmns_tests.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -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(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 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(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(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; diff --git a/src/test/evo_simplifiedmns_tests.cpp b/src/test/evo_simplifiedmns_tests.cpp index 9129f8e8db..3f632d2824 100644 --- a/src/test/evo_simplifiedmns_tests.cpp +++ b/src/test/evo_simplifiedmns_tests.cpp @@ -28,7 +28,7 @@ BOOST_AUTO_TEST_CASE(simplifiedmns_merkleroots) std::vector vecBytes{static_cast(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; diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 7cd8e6c8c4..220b4a1cf6 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -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); +} diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h index 31b9ae9aae..a3c5c1c671 100644 --- a/src/test/util/setup_common.h +++ b/src/test/util/setup_common.h @@ -152,6 +152,11 @@ struct TestChainDIP3BeforeActivationSetup : public TestChainSetup TestChainDIP3BeforeActivationSetup() : TestChainSetup(430) {} }; +struct TestChainV19BeforeActivationSetup : public TestChainSetup +{ + TestChainV19BeforeActivationSetup(); +}; + class CTxMemPoolEntry; struct TestMemPoolEntryHelper diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 199f3dea3d..9169fe68a0 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -19,7 +19,6 @@ #include #include -#include #include #include #include @@ -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; } diff --git a/src/txmempool.h b/src/txmempool.h index 58c609dd04..33458b9973 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -33,8 +34,6 @@ #include #include -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); diff --git a/src/validation.cpp b/src/validation.cpp index a1a53d9633..3c39f30a60 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -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; } diff --git a/test/functional/feature_dip3_v19.py b/test/functional/feature_dip3_v19.py index 855bb76451..497dc82fc3 100755 --- a/test/functional/feature_dip3_v19.py +++ b/test/functional/feature_dip3_v19.py @@ -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() From 013445a304205726aa1ca499bba0e793811b0914 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 6 Jun 2023 11:39:16 +0700 Subject: [PATCH 12/20] fix: follow-up #5403: remove double serialization in CDeterministicMNState (#5413) ## Issue being fixed or feature implemented Member obj.keyIDOwner is read & write twice ## What was done? Fixed: it is serialized once ## How Has This Been Tested? Unit/functional tests in CI ## Breaking Changes Data format in database changed in incompatible way ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone --- src/evo/dmnstate.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/evo/dmnstate.h b/src/evo/dmnstate.h index fb8e17c040..fc71cbbf7e 100644 --- a/src/evo/dmnstate.h +++ b/src/evo/dmnstate.h @@ -231,7 +231,6 @@ public: obj.nRevocationReason, obj.confirmedHash, obj.confirmedHashWithProRegTxHash, - obj.keyIDOwner, obj.keyIDOwner); READWRITE(CBLSLazyPublicKeyVersionWrapper(const_cast(obj.pubKeyOperator), obj.nVersion == CProRegTx::LEGACY_BLS_VERSION)); READWRITE( From ad09590feb446a0cca43eb511b4f685911ce2e9a Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Tue, 6 Jun 2023 19:31:11 +0300 Subject: [PATCH 13/20] fix: reset chainman to allow reindex on failure (#5405) current develop fails to reindex whenever there is an issue at node start (prints `should not be overwriting a chainstate` in `debug.log`) reset chainman to allow it re-initialize chainstate simulated an issue with ``` if (!fReset) { strLoadError = _("DEBUG"); break; } ``` should not be any but pls test - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --- src/init.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/init.cpp b/src/init.cpp index 3d4a436ca7..e391bb4efc 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2019,6 +2019,8 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc LOCK(cs_main); node.evodb.reset(); node.evodb = std::make_unique(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; From a39dd04f7a3709ce08dd13a36bd3bc33d6f99e2d Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Sun, 11 Jun 2023 10:00:10 +0300 Subject: [PATCH 14/20] fix: multiple 5403 followups (#5424) - CBLSLazyWrapper is doing to much and not enough at the same time - nVersion assignment in CDeterministicMNState(Diff) is incomplete - pubKeyOperator deserialization needs nVersion but nVersion is deser-ed much later - protx rpcs are implicitly converting pubKeyOperator (by forcing nVersion=2), they shouldn't do that pls see individual commits - [x] run tests locally - [x] reindex on testnet: - [x] with and without `--assumevalid=0` to the tip - [x] with 19.1 almost to the forkpoint, then with this version - [x] reindex on mainnet: - [x] with and without `--assumevalid=0` to the tip - [x] with 19.1 to height 1100000+, then with this version might need reindexing if you were running develop on testnet already - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --- src/bls/bls.h | 28 +++++++++++++++----------- src/evo/deterministicmns.cpp | 10 ++++++---- src/evo/dmnstate.cpp | 5 +++-- src/evo/dmnstate.h | 11 ++++++++++- src/evo/providertx.cpp | 6 ++++++ src/evo/simplifiedmns.cpp | 5 +++-- src/rpc/evo.cpp | 38 +++++++++++++++++++----------------- 7 files changed, 65 insertions(+), 38 deletions(-) diff --git a/src/bls/bls.h b/src/bls/bls.h index 840b81d8a9..64c96c8742 100644 --- a/src/bls/bls.h +++ b/src/bls/bls.h @@ -434,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; @@ -456,6 +457,7 @@ public: { std::unique_lock l(mutex); if (!objInitialized && !bufValid) { + vecBytes.resize(BLSObject::SerSize); std::fill(vecBytes.begin(), vecBytes.end(), 0); } else if (!bufValid || (bufLegacyScheme != specificLegacyScheme)) { vecBytes = obj.ToByteVector(specificLegacyScheme); @@ -508,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; } @@ -547,6 +542,7 @@ public: { std::unique_lock l(mutex); if (!objInitialized && !bufValid) { + vecBytes.resize(BLSObject::SerSize); std::fill(vecBytes.begin(), vecBytes.end(), 0); hash.SetNull(); } else if (!bufValid) { @@ -562,6 +558,16 @@ public: return hash; } + bool IsLegacy() const + { + return bufLegacyScheme; + } + + void SetLegacy(bool specificLegacyScheme) + { + bufLegacyScheme = specificLegacyScheme; + } + std::string ToString() const { return Get().ToString(bufLegacyScheme); diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 0b243ed0b2..503472c7a6 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -519,7 +519,6 @@ void CDeterministicMNList::UpdateMN(const CDeterministicMN& oldDmn, const std::s { auto dmn = std::make_shared(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. @@ -541,13 +540,14 @@ void CDeterministicMNList::UpdateMN(const CDeterministicMN& oldDmn, const std::s 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); } @@ -899,8 +899,10 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C // 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 = proTx.pubKeyOperator; newState->keyIDVoting = proTx.keyIDVoting; newState->scriptPayout = proTx.scriptPayout; diff --git a/src/evo/dmnstate.cpp b/src/evo/dmnstate.cpp index d2be5cefbf..ff755b2771 100644 --- a/src/evo/dmnstate.cpp +++ b/src/evo/dmnstate.cpp @@ -26,9 +26,9 @@ 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, + nVersion, nRegisteredHeight, nLastPaidHeight, nPoSePenalty, nPoSeRevivedHeight, nPoSeBanHeight, nRevocationReason, EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.ToString(), EncodeDestination(PKHash(keyIDVoting)), addr.ToStringIPPort(false), payoutAddress, operatorPayoutAddress); } @@ -36,6 +36,7 @@ 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); diff --git a/src/evo/dmnstate.h b/src/evo/dmnstate.h index fc71cbbf7e..59ca4d6426 100644 --- a/src/evo/dmnstate.h +++ b/src/evo/dmnstate.h @@ -245,7 +245,8 @@ public: void ResetOperatorFields() { - pubKeyOperator.Set(CBLSPublicKey(), bls::bls_legacy_scheme.load()); + nVersion = CProRegTx::LEGACY_BLS_VERSION; + pubKeyOperator = CBLSLazyPublicKey(); addr = CService(); scriptOperatorPayout = CScript(); nRevocationReason = CProUpRevTx::REASON_NOT_SPECIFIED; @@ -343,18 +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)) {\ + SER_READ(obj, read_pubkey = true); \ READWRITE(CBLSLazyPublicKeyVersionWrapper(const_cast(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 diff --git a/src/evo/providertx.cpp b/src/evo/providertx.cpp index d7d3ba8b0f..6a5cbf338b 100644 --- a/src/evo/providertx.cpp +++ b/src/evo/providertx.cpp @@ -26,6 +26,9 @@ maybe_error CProRegTx::IsTriviallyValid(bool is_bls_legacy_scheme) const 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"}; } @@ -117,6 +120,9 @@ maybe_error CProUpRegTx::IsTriviallyValid(bool is_bls_legacy_scheme) const 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"}; } diff --git a/src/evo/simplifiedmns.cpp b/src/evo/simplifiedmns.cpp index 202396047e..e613d071cd 100644 --- a/src/evo/simplifiedmns.cpp +++ b/src/evo/simplifiedmns.cpp @@ -57,14 +57,15 @@ 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)); diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index 5695a42488..bb6f648066 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -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"); - ptx.pubKeyOperator.Set(ParseBLSPubKey(request.params[paramIdx + 2].get_str(), "operator BLS address", ptx.nVersion == CProRegTx::LEGACY_BLS_VERSION), ptx.nVersion == CProRegTx::LEGACY_BLS_VERSION); + 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() != "") { @@ -904,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"); @@ -943,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")); @@ -1029,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; 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.Set(ParseBLSPubKey(request.params[1].get_str(), "operator BLS address", ptx.nVersion == CProUpRegTx::LEGACY_BLS_VERSION), ptx.nVersion == CProRegTx::LEGACY_BLS_VERSION); + // 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"); } From 3ac3c1ad3c84aa38b59b47f956da4409ffba5e87 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Sun, 11 Jun 2023 20:29:00 +0300 Subject: [PATCH 15/20] feat: store protx version in CSimplifiedMNListEntry and use it to ser/deser pubKeyOperator (#5397) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mobile wallets would have to convert 4k+ pubkeys at the V19 fork point and it's a pretty hard job for them that can easily take 10-15 seconds if not more. Also after the HF, if a masternode list is requested from before the HF, the operator keys come in basic scheme, but the merkelroot was calculated with legacy. From mobile team work it wasn't possible to convert all operator keys to legacy and then calculate the correct merkleroot. ~This PR builds on top of ~#5392~ #5403 (changes that belong to this PR: 26f7e966500bdea4c604f1d16716b40b366fc707 and 4b42dc8fcee3354afd82ce7e3a72ebe1659f5f22) and aims to solve both of these issues.~ cc @hashengineering @QuantumExplorer Introduce `nVersion` on p2p level for every CSimplifiedMNListEntry. Set `nVersion` to the same value we have it in CDeterministicMNState i.e. pubkey serialization would not be via basic scheme only after the V19 fork, it would match the way it’s serialized on-chain/in CDeterministicMNState for that specific MN. run tests NOTE: `testnet` is going to re-fork at v19 forkpoint because `merkleRootMNList` is not going to match - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --- src/evo/cbtx.cpp | 3 +-- src/evo/deterministicmns.cpp | 5 ---- src/evo/simplifiedmns.cpp | 11 ++++---- src/evo/simplifiedmns.h | 12 +++++---- src/version.h | 5 +++- test/functional/feature_dip3_v19.py | 2 +- .../feature_dip4_coinbasemerkleroots.py | 2 +- test/functional/feature_llmq_hpmn.py | 2 +- test/functional/test_framework/messages.py | 26 ++++++++++--------- 9 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/evo/cbtx.cpp b/src/evo/cbtx.cpp index 8f4b4fb60c..b44b4f4d90 100644 --- a/src/evo/cbtx.cpp +++ b/src/evo/cbtx.cpp @@ -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); diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 503472c7a6..2838d7e1e4 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -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)); diff --git a/src/evo/simplifiedmns.cpp b/src/evo/simplifiedmns.cpp index e613d071cd..5bdca1047a 100644 --- a/src/evo/simplifiedmns.cpp +++ b/src/evo/simplifiedmns.cpp @@ -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) @@ -69,7 +70,7 @@ void CSimplifiedMNListEntry::ToJson(UniValue& obj, bool extended) const 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); @@ -103,15 +104,13 @@ CSimplifiedMNList::CSimplifiedMNList(const std::vector& }); } -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(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(dmn); }); std::sort(mnList.begin(), mnList.end(), [&](const std::unique_ptr& a, const std::unique_ptr& b) { diff --git a/src/evo/simplifiedmns.h b/src/evo/simplifiedmns.h index 77165c56b6..dab50a2e96 100644 --- a/src/evo/simplifiedmns.h +++ b/src/evo/simplifiedmns.h @@ -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& 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 deletedMNs; std::vector mnList; - uint16_t nVersion{LEGACY_BLS_VERSION}; + uint16_t nVersion{CURRENT_VERSION}; std::vector> deletedQuorums; // p std::vector newQuorums; diff --git a/src/version.h b/src/version.h index a2114db494..724509824f 100644 --- a/src/version.h +++ b/src/version.h @@ -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 diff --git a/test/functional/feature_dip3_v19.py b/test/functional/feature_dip3_v19.py index 497dc82fc3..8bbf131818 100755 --- a/test/functional/feature_dip3_v19.py +++ b/test/functional/feature_dip3_v19.py @@ -166,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) diff --git a/test/functional/feature_dip4_coinbasemerkleroots.py b/test/functional/feature_dip4_coinbasemerkleroots.py index 5d04787c3c..ec69bde6a8 100755 --- a/test/functional/feature_dip4_coinbasemerkleroots.py +++ b/test/functional/feature_dip4_coinbasemerkleroots.py @@ -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) diff --git a/test/functional/feature_llmq_hpmn.py b/test/functional/feature_llmq_hpmn.py index 24c85ddb1f..882a9203aa 100755 --- a/test/functional/feature_llmq_hpmn.py +++ b/test/functional/feature_llmq_hpmn.py @@ -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) diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py index 6b128990d0..7cb9ce6b93 100755 --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -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(" Date: Mon, 12 Jun 2023 10:58:44 +0300 Subject: [PATCH 16/20] chore: update defaultAssumeValid, nMinimumChainWork, checkpointData and chainTxData for testnet (#5428) Having these above v19 forkpoint (850100) would result in v19.2 nodes forking at the wrong height (864000) when reindexing without `--assumevalid=<0 or some pre-v19 block height>` Go back to pre-v19 block (850000) in chainparams reindex n/a - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --- src/chainparams.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 13816ca0d6..7814b019f6 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -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("0x00000000000000000000000000000000000000000000000002d68c89409f9383"); // 850000 // By default assume that the signatures in ancestors of this block are valid. - consensus.defaultAssumeValid = uint256S("0x0000005c35514190ef3c38d322f69412553dc7e1107ed5f92adc2935b90acc51"); // 864000 + consensus.defaultAssumeValid = uint256S("0x0000003eddb94218e7a3f41b2ac6e26143f8a748b50cd26e86bdbbab9ebe50aa"); // 850000 pchMessageStart[0] = 0xce; pchMessageStart[1] = 0xe2; @@ -568,16 +568,16 @@ public: {470000, uint256S("0x0000009303aeadf8cf3812f5c869691dbd4cb118ad20e9bf553be434bafe6a52")}, {794950, uint256S("0x000001860e4c7248a9c5cc3bc7106041750560dc5cd9b3a2641b49494bcff5f2")}, {808000, uint256S("0x00000104cb60a2b5e00a8a4259582756e5bf0dca201c0993c63f0e54971ea91a")}, - {864000, uint256S("0x0000005c35514190ef3c38d322f69412553dc7e1107ed5f92adc2935b90acc51")}, + {850000, uint256S("0x0000003eddb94218e7a3f41b2ac6e26143f8a748b50cd26e86bdbbab9ebe50aa")}, } }; - // getchaintxstats 17280 0000005c35514190ef3c38d322f69412553dc7e1107ed5f92adc2935b90acc51 + // getchaintxstats 17280 0000003eddb94218e7a3f41b2ac6e26143f8a748b50cd26e86bdbbab9ebe50aa chainTxData = ChainTxData{ - 1680868209, // * UNIX timestamp of last known number of transactions (Block 771537) - 5847013, // * total number of transactions between genesis and that timestamp + 1678858966, // * UNIX timestamp of last known number of transactions (Block 850000) + 5796306, // * 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.01077368401768078, // * estimated number of transactions per second after that timestamp }; } }; From 7477d839cd32d5309114d222a2cf9ea2216ff925 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Mon, 12 Jun 2023 10:57:49 +0300 Subject: [PATCH 17/20] Merge pull request #5425 from UdjinM6/multi_fixes fix: Various small fixes --- src/bip39.cpp | 4 ++-- src/bip39.h | 2 +- src/evo/specialtx.cpp | 2 +- src/init.cpp | 4 ++-- src/llmq/utils.cpp | 2 +- src/pow.cpp | 4 ++++ src/qt/bitcoingui.cpp | 2 +- src/qt/optionsdialog.cpp | 13 ++++++++----- src/qt/optionsdialog.h | 1 + src/qt/sendcoinsdialog.cpp | 1 + src/test/evo_utils_tests.cpp | 6 +++--- src/unordered_lru_cache.h | 2 +- src/wallet/walletdb.cpp | 4 +++- 13 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/bip39.cpp b/src/bip39.cpp index 2134f98dec..61c19bdbd6 100644 --- a/src/bip39.cpp +++ b/src/bip39.cpp @@ -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()); } diff --git a/src/bip39.h b/src/bip39.h index 9c6426b244..30f3a6880e 100644 --- a/src/bip39.h +++ b/src/bip39.h @@ -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); }; diff --git a/src/evo/specialtx.cpp b/src/evo/specialtx.cpp index 4dd9c0203c..6a1820dd9e 100644 --- a/src/evo/specialtx.cpp +++ b/src/evo/specialtx.cpp @@ -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; } diff --git a/src/init.cpp b/src/init.cpp index e391bb4efc..026fcc9e10 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -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=", "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=", "Set the private key to be used for signing spork messages.", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-sporkkey=", "Set the private key to be used for signing spork messages.", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::DEBUG_TEST); argsman.AddArg("-uacomment=", "Append comment to the user agent string", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); SetupChainParamsBaseOptions(argsman); argsman.AddArg("-llmq-data-recovery=", strprintf("Enable automated quorum data recovery (default: %u)", llmq::DEFAULT_ENABLE_QUORUM_DATA_RECOVERY), ArgsManager::ALLOW_ANY, OptionsCategory::MASTERNODE); argsman.AddArg("-llmq-qvvec-sync=:", strprintf("Defines from which LLMQ type the masternode should sync quorum verification vectors. Can be used multiple times with different LLMQ types. : %d (sync always from all quorums of the type defined by ), %d (sync from all quorums of the type defined by 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=", "Set the masternode BLS private key and enable the client to act as a masternode", ArgsManager::ALLOW_ANY, OptionsCategory::MASTERNODE); + argsman.AddArg("-masternodeblsprivkey=", "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=", "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); diff --git a/src/llmq/utils.cpp b/src/llmq/utils.cpp index ce0fba766c..0d43c311d4 100644 --- a/src/llmq/utils.cpp +++ b/src/llmq/utils.cpp @@ -573,7 +573,7 @@ uint256 BuildCommitmentHash(Consensus::LLMQType llmqType, const uint256& blockHa const std::vector& validMembers, const CBLSPublicKey& pubKey, const uint256& vvecHash) { - CHashWriter hw(SER_NETWORK, 0); + CHashWriter hw(SER_GETHASH, 0); hw << llmqType; hw << blockHash; hw << DYNBITSET(validMembers); diff --git a/src/pow.cpp b/src/pow.cpp index 9135ebb42a..ed014aeba7 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -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) { diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 9851801a9f..4b244637f0 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -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); diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 440685835b..586a7aae74 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -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(); diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h index 56042bbadc..3eceb2f0f1 100644 --- a/src/qt/optionsdialog.h +++ b/src/qt/optionsdialog.h @@ -89,6 +89,7 @@ private: QString previousTheme; AppearanceWidget* appearance; bool fCoinJoinEnabledPrev{false}; + bool m_enable_wallet{false}; void showEvent(QShowEvent* event) override; }; diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 6e07593fb4..986a30061f 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -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(); diff --git a/src/test/evo_utils_tests.cpp b/src/test/evo_utils_tests.cpp index 42b0001642..5ff3c196a6 100644 --- a/src/test/evo_utils_tests.cpp +++ b/src/test/evo_utils_tests.cpp @@ -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) diff --git a/src/unordered_lru_cache.h b/src/unordered_lru_cache.h index 8a364b8547..7208737dde 100644 --- a/src/unordered_lru_cache.h +++ b/src/unordered_lru_cache.h @@ -36,7 +36,6 @@ public: template 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(v), accessCounter++)); @@ -44,6 +43,7 @@ public: it->second.first = std::forward(v); it->second.second = accessCounter++; } + truncate_if_needed(); } void emplace(const Key& key, Value&& v) diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index fcddef2ddb..677129d3c0 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -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) { From f391d7cac33ac443eed98fdd91c905cf53fd2726 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Mon, 12 Jun 2023 21:47:31 +0300 Subject: [PATCH 18/20] chore: update defaultAssumeValid, nMinimumChainWork, checkpointData and chainTxData for testnet (again) (#5430) Same as #5428 but with a lower block number this time. This should let us simply reorg testnet with 18.2.2 at deeper blocks instead of bumping v19 testnet activation params for 19.2. n/a - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --- src/chainparams.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 7814b019f6..8a121c9eb6 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -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("0x00000000000000000000000000000000000000000000000002d68c89409f9383"); // 850000 + consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000000002d68c6dc9ca04f3"); // 840000 // By default assume that the signatures in ancestors of this block are valid. - consensus.defaultAssumeValid = uint256S("0x0000003eddb94218e7a3f41b2ac6e26143f8a748b50cd26e86bdbbab9ebe50aa"); // 850000 + 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")}, - {850000, uint256S("0x0000003eddb94218e7a3f41b2ac6e26143f8a748b50cd26e86bdbbab9ebe50aa")}, + {840000, uint256S("0x000000cd7c3084499912ae893125c13e8c3c656abb6e511dcec6619c3d65a510")}, } }; - // getchaintxstats 17280 0000003eddb94218e7a3f41b2ac6e26143f8a748b50cd26e86bdbbab9ebe50aa + // getchaintxstats 17280 000000cd7c3084499912ae893125c13e8c3c656abb6e511dcec6619c3d65a510 chainTxData = ChainTxData{ - 1678858966, // * UNIX timestamp of last known number of transactions (Block 850000) - 5796306, // * 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.01077368401768078, // * estimated number of transactions per second after that timestamp + 0.01120953982471268, // * estimated number of transactions per second after that timestamp }; } }; From 225398dafc9a48b7e1c871585f718130325c34b7 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Tue, 13 Jun 2023 17:24:19 +0300 Subject: [PATCH 19/20] fix: off-by-one in the way we use v19 activation helpers (#5431) Some conditions won't trigger when reorging exactly from the forkpoint pls see individual commits, tl;dr: you can't get correct results with `GetAncestor` cause the answer is in the future reorg to 850000 and back on testnet ``` invalidateblock 0000003eddb94218e7a3f41b2ac6e26143f8a748b50cd26e86bdbbab9ebe50aa reconsiderblock 0000003eddb94218e7a3f41b2ac6e26143f8a748b50cd26e86bdbbab9ebe50aa ``` this fails on develop and work with this patch n/a - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --- src/evo/deterministicmns.cpp | 7 +------ src/evo/specialtxman.cpp | 4 ++-- src/governance/object.cpp | 2 +- src/governance/vote.cpp | 2 +- src/llmq/utils.cpp | 19 ++++++++++++------- src/llmq/utils.h | 1 + 6 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 2838d7e1e4..22ec852638 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -643,13 +643,8 @@ bool CDeterministicMNManager::ProcessBlock(const CBlock& block, const CBlockInde oldList = GetListForBlock(pindex->pprev); diff = oldList.BuildDiff(newList); - // NOTE: The block next to the activation is the one that is using new rules. - // If the fork was activated at pindex->prev block then current one is the first one - // using new rules. Save mn list snapsot for this block. - bool v19_just_activated = pindex->pprev == llmq::utils::V19ActivationIndex(pindex); - 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", diff --git a/src/evo/specialtxman.cpp b/src/evo/specialtxman.cpp index e4afa21314..18aac604e3 100644 --- a/src/evo/specialtxman.cpp +++ b/src/evo/specialtxman.cpp @@ -154,7 +154,7 @@ bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, ll nTimeMerkle += nTime5 - nTime4; LogPrint(BCLog::BENCHMARK, " - CheckCbTxMerkleRoots: %.2fms [%.2fs]\n", 0.001 * (nTime5 - nTime4), nTimeMerkle * 0.000001); - if (llmq::utils::V19ActivationIndex(pindex) == pindex) { + 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); @@ -175,7 +175,7 @@ bool UndoSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, llmq: auto bls_legacy_scheme = bls::bls_legacy_scheme.load(); try { - if (llmq::utils::V19ActivationIndex(pindex) == pindex) { + 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); diff --git a/src/governance/object.cpp b/src/governance/object.cpp index 1e5b936779..ad8fe25b4e 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -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"); diff --git a/src/governance/vote.cpp b/src/governance/vote.cpp index 8482ed29bc..cea35b32d0 100644 --- a/src/governance/vote.cpp +++ b/src/governance/vote.cpp @@ -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"); diff --git a/src/llmq/utils.cpp b/src/llmq/utils.cpp index 0d43c311d4..cc7db22e6a 100644 --- a/src/llmq/utils.cpp +++ b/src/llmq/utils.cpp @@ -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() diff --git a/src/llmq/utils.h b/src/llmq/utils.h index 289d6b4bc3..91ff322f10 100644 --- a/src/llmq/utils.h +++ b/src/llmq/utils.h @@ -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` From 75fe05ef0ed9a2abd07bca9067349be4c1d43459 Mon Sep 17 00:00:00 2001 From: pasta Date: Sun, 11 Jun 2023 23:24:01 -0500 Subject: [PATCH 20/20] chore: update version to 19.2 rc.1 --- configure.ac | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 3092793fd7..2a977535b7 100644 --- a/configure.ac +++ b/configure.ac @@ -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]])