diff --git a/contrib/flatpak/org.dash.dash-core.metainfo.xml b/contrib/flatpak/org.dash.dash-core.metainfo.xml index 66e742659443..ba72128f9769 100644 --- a/contrib/flatpak/org.dash.dash-core.metainfo.xml +++ b/contrib/flatpak/org.dash.dash-core.metainfo.xml @@ -21,6 +21,7 @@ + diff --git a/doc/man/dash-cli.1 b/doc/man/dash-cli.1 index 8bf92ce8161b..9fda610b7d09 100644 --- a/doc/man/dash-cli.1 +++ b/doc/man/dash-cli.1 @@ -1,7 +1,7 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. -.TH DASH-CLI "1" "July 2026" "dash-cli v23.1.8" "User Commands" +.TH DASH-CLI "1" "October 2026" "dash-cli v23.1.9" "User Commands" .SH NAME -dash-cli \- manual page for dash-cli v23.1.8 +dash-cli \- manual page for dash-cli v23.1.9 .SH SYNOPSIS .B dash-cli [\fI\,options\/\fR] \fI\, \/\fR[\fI\,params\/\fR] \fI\,Send command to Dash Core\/\fR @@ -15,7 +15,7 @@ dash-cli \- manual page for dash-cli v23.1.8 .B dash-cli [\fI\,options\/\fR] \fI\,help Get help for a command\/\fR .SH DESCRIPTION -Dash Core RPC client version v23.1.8 +Dash Core RPC client version v23.1.9 .SH OPTIONS .HP \-? diff --git a/doc/man/dash-qt.1 b/doc/man/dash-qt.1 index 00cba193fda8..305803d723e2 100644 --- a/doc/man/dash-qt.1 +++ b/doc/man/dash-qt.1 @@ -1,12 +1,12 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. -.TH DASH-QT "1" "July 2026" "dash-qt v23.1.8" "User Commands" +.TH DASH-QT "1" "October 2026" "dash-qt v23.1.9" "User Commands" .SH NAME -dash-qt \- manual page for dash-qt v23.1.8 +dash-qt \- manual page for dash-qt v23.1.9 .SH SYNOPSIS .B dash-qt [\fI\,command-line options\/\fR] [\fI\,URI\/\fR] .SH DESCRIPTION -Dash Core version v23.1.8 +Dash Core version v23.1.9 .PP Optional URI is a Dash address in BIP21 URI format. .SH OPTIONS diff --git a/doc/man/dash-tx.1 b/doc/man/dash-tx.1 index 7b587eeb9f19..906c258e33ca 100644 --- a/doc/man/dash-tx.1 +++ b/doc/man/dash-tx.1 @@ -1,7 +1,7 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. -.TH DASH-TX "1" "July 2026" "dash-tx v23.1.8" "User Commands" +.TH DASH-TX "1" "October 2026" "dash-tx v23.1.9" "User Commands" .SH NAME -dash-tx \- manual page for dash-tx v23.1.8 +dash-tx \- manual page for dash-tx v23.1.9 .SH SYNOPSIS .B dash-tx [\fI\,options\/\fR] \fI\, \/\fR[\fI\,commands\/\fR] \fI\,Update hex-encoded dash transaction\/\fR @@ -9,7 +9,7 @@ dash-tx \- manual page for dash-tx v23.1.8 .B dash-tx [\fI\,options\/\fR] \fI\,-create \/\fR[\fI\,commands\/\fR] \fI\,Create hex-encoded dash transaction\/\fR .SH DESCRIPTION -Dash Core dash\-tx utility version v23.1.8 +Dash Core dash\-tx utility version v23.1.9 .SH OPTIONS .HP \-? diff --git a/doc/man/dash-util.1 b/doc/man/dash-util.1 index 3a47b0b8037a..55dc5922e6e6 100644 --- a/doc/man/dash-util.1 +++ b/doc/man/dash-util.1 @@ -1,12 +1,12 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. -.TH DASH-UTIL "1" "July 2026" "dash-util v23.1.8" "User Commands" +.TH DASH-UTIL "1" "October 2026" "dash-util v23.1.9" "User Commands" .SH NAME -dash-util \- manual page for dash-util v23.1.8 +dash-util \- manual page for dash-util v23.1.9 .SH SYNOPSIS .B dash-util [\fI\,options\/\fR] [\fI\,commands\/\fR] \fI\,Do stuff\/\fR .SH DESCRIPTION -Dash Core dash\-util utility version v23.1.8 +Dash Core dash\-util utility version v23.1.9 .SH OPTIONS .HP \-? diff --git a/doc/man/dash-wallet.1 b/doc/man/dash-wallet.1 index ffeaccdb710f..1ddce2058fde 100644 --- a/doc/man/dash-wallet.1 +++ b/doc/man/dash-wallet.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. -.TH DASH-WALLET "1" "July 2026" "dash-wallet v23.1.8" "User Commands" +.TH DASH-WALLET "1" "October 2026" "dash-wallet v23.1.9" "User Commands" .SH NAME -dash-wallet \- manual page for dash-wallet v23.1.8 +dash-wallet \- manual page for dash-wallet v23.1.9 .SH DESCRIPTION -Dash Core dash\-wallet version v23.1.8 +Dash Core dash\-wallet version v23.1.9 .PP dash\-wallet is an offline tool for creating and interacting with Dash Core wallet files. By default dash\-wallet will act on wallets in the default mainnet wallet directory in the datadir. diff --git a/doc/man/dashd.1 b/doc/man/dashd.1 index 3f2e77656af2..cfddccb0d144 100644 --- a/doc/man/dashd.1 +++ b/doc/man/dashd.1 @@ -1,12 +1,12 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. -.TH DASHD "1" "July 2026" "dashd v23.1.8" "User Commands" +.TH DASHD "1" "October 2026" "dashd v23.1.9" "User Commands" .SH NAME -dashd \- manual page for dashd v23.1.8 +dashd \- manual page for dashd v23.1.9 .SH SYNOPSIS .B dashd [\fI\,options\/\fR] \fI\,Start Dash Core\/\fR .SH DESCRIPTION -Dash Core version v23.1.8 +Dash Core version v23.1.9 .SH OPTIONS .HP \-? diff --git a/doc/release-notes.md b/doc/release-notes.md index 2a8d804abe00..2f1c11f9b3bf 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -1,8 +1,10 @@ -# Dash Core version v23.1.8 +# Dash Core version v23.1.9 + +This is a new patch version release. It fixes a consensus-affecting +transaction-encoding bug, several remotely reachable crashes and +denial-of-service issues in the masternode, DKG, signing and governance paths, +and backports a number of hardening and robustness fixes from later releases. -This is a new patch version release, fixing three remotely reachable crashes and -bringing further hardening of the peer-to-peer message handlers along with -networking, RPC and build fixes. Upgrading is **strongly recommended** for all nodes, and required for masternodes. @@ -28,98 +30,91 @@ require a reindex. # Release Notes -## Critical fixes - -This release fixes three crashes that a remote party could trigger. None of -them affect consensus rules or put funds at risk, but each one can take a node -offline, so all operators should upgrade promptly. - -- Fixed a crash while removing provider transactions that a masternode's - operator-key change invalidates. Those transactions are collected before any - of them are removed, so when one was an in-mempool descendant of another it - was already erased along with its ancestor, and the stale entry was then - dereferenced. Such entries are now skipped. This is reachable whenever a block - carries a provider registrar update or revocation for a masternode that has - chained service updates pending in the mempool. -- Fixed a crash caused by an unvalidated LLMQ type in a `qsigshare` message. A - masternode that received a signature share naming an LLMQ type its chain does - not register would index a per-type quorum cache that is only populated for - known types, aborting the process. Unregistered types are now rejected before - the lookup, and the affected cache lookups no longer create missing entries. -- Fixed a crash caused by a quorum commitment naming a block with no parent, - such as the genesis block. The parentless block index reached a non-null - precondition and terminated the process instead of failing validation, which - no exception handler could contain. Commitments with a parentless quorum base - block are now rejected, and the LLMQ activation check treats a null - predecessor as "not enabled" rather than a contract violation. - -## Security - -This release continues the hardening of peer-to-peer message handlers against -denial-of-service from remote peers. These issues do not affect consensus and do -not put funds at risk, but they could be used to crash or degrade nodes - -masternodes in particular - so upgrading is recommended. - -- LLMQ / signing: the queues of not-yet-verified recovered signatures and - signature shares are now bounded, and the vectors carried by the QSIGSHARE, - QSIGSESANN, QSIGSHARESINV, QGETSIGSHARES and QBSIGSHARES messages are bounded - before any allocation or decoding takes place. The number of signing share - sessions a single peer may announce is also capped, so a peer can no longer - grow that per-peer state without limit (dash#7351). -- LLMQ / DKG: the number of encrypted contribution blobs in a DKG contribution - is now checked against the quorum's lower bound as well as its upper bound. -- LLMQ / quorum data: the verification vector and encrypted contribution - vectors in QDATA responses are validated against their expected sizes before - any BLS decoding is performed. -- Transaction relay: an oversized `notfound` message is now penalised rather - than silently ignored (dash#7348). -- ChainLocks: the cache of seen ChainLock signatures is now bounded. -- Governance: per-object vote sync requests are now throttled per peer, and - governance object and vote responses are only accepted from a peer if that - peer announced them or they were requested from it, using the net-layer - per-peer request tracker. Governance vote signatures are bounded when read - from the network and must use one of the two legitimate encodings. -- CoinJoin: the vectors carried by CoinJoin mixing messages are bounded before - allocation, and a non-participant can no longer abort another session's - signing phase. An invalid `dstx` message now carries a misbehaviour score - instead of being dropped for free (dash#7347). -- Bloom filters: filterload and filteradd payloads are bounded before - allocation. -- Sporks: spork signatures are bounded during deserialization, and malformed - spork messages now attribute misbehaviour to the sending peer. -- Compact block relay: batched hardening backported from upstream Bitcoin Core - (dash#7398), including detection of mutated blocks as a defence-in-depth - measure. - -## RPC - -- `protx listdiff` no longer reports an always-zero `platformP2PPort` / - `platformHTTPPort` for masternodes registered with extended addresses; the - live Platform ports are reported instead. - -## GUI - -- The PoSe score column is no longer hidden together with banned masternodes in - the masternode list. -- Fixed an abort when scaling widgets whose font was set in pixels rather than - points (for example by a stylesheet's `font-size: Npx`); such fonts are now - converted to a point size instead of being assumed to have one (dash#7465). - -## Build and CI - -- Fixed a CMake compatibility error when building the freetype dependency with - newer CMake (dash#7372). -- Stabilized the `-par` / `-parbls` help text (and the generated man pages) so - they no longer embed the core count of the build machine. -- Updated GitHub Actions pins for the Node 24 runtime. -- Fixed the circular-dependencies lint script under Python 3.15. - -## Tests - -- Governance inventory cache coverage moved from a functional test to unit - tests, and governance vote test fixtures are now wire-valid. - -# v23.1.8 Change log +## Consensus + +- Transactions whose version field is negative are now rejected once DIP3 is + active. The version and type share one 32-bit field; a negative version was + sign-extended when a transaction was re-serialized, so a block containing such + a transaction could be accepted by a node that received the original bytes and + rejected by every node that received the re-serialized form, leading to a + persistent chain split. The new rule rejects these transactions uniformly. No + valid or historical transaction is affected and transaction ids are unchanged. + +## Crash fixes + +These issues do not affect consensus rules or put funds at risk, but each one +can take a node offline. + +- Fixed an abort during the DKG when a quorum member sent a contribution whose + decrypted secret-key share was invalid: the share reached an aggregation + assertion and terminated the process on every honest member of the quorum. + Invalid shares are now rejected before aggregation. +- Fixed block-template failures caused by provider transactions: a + `ProUpServTx` with an invalid type, a `ProRegTx` reusing collateral already + referenced by an in-mempool update, and a pending owner key colliding with a + Platform node id are now rejected at mempool acceptance. +- Bounded the masternode-list caches by size so a peer cannot drive unbounded + memory growth between blocks. +- Bounded `DomainPort` deserialization so a small provider transaction can no + longer make the node allocate and zero very large buffers while holding + `cs_main`. +- Decoding an unknown-version provider transaction through the verbose decode + RPCs no longer dereferences a missing network-info object. +- `masternode payments` no longer aborts at the genesis block, and the + masternode-list RPCs no longer cache a list for a block outside the active + chain. +- Evonodes no longer abort at credit-pool snapshot boundaries when the pool is + first constructed outside a block connection (for example by + `getassetunlockstatuses`). +- Flat-file (block and undo) positions are now stored and seeked as 64-bit + values. Block-undo data can be several times the size of its block, so a busy + chain could push a data file past the point a 32-bit offset can address; the + write and seek paths no longer narrow the position to 32 bits, which avoided a + silent wrap (and, on Windows, a failure past 2 GiB). + +## InstantSend + +- Provider (masternode) transactions are no longer given InstantSend locks. + Locking them served no purpose and interacted badly with lock eviction on + mempool removal. Software that waited for an InstantSend lock on a provider + transaction should wait for a ChainLock or for confirmations instead. + +## Security and denial-of-service hardening + +These issues do not affect consensus and do not put funds at risk. + +- DKG contribution verification: a single verification-vector coefficient + shared between two members no longer fails the whole verification batch. + Previously that marked unrelated honest members' contributions invalid and + caused them to be complained about; contributions are now verified + individually, so only the offending member is affected. +- Signature-share batch verification is now scoped per sender, so one peer can + no longer get an honest peer penalised or have a forged share stored in place + of an honest one. Malformed detached quorum share batches are rejected, and + signing intake is gated against the active-quorum set before any quorum is + retrieved. +- Governance: an orphan vote now requires a valid masternode signature before + it is cached, the number of orphan parent objects re-requested per tick is + bounded, governance vote signature checks are memoised to bound the cost of a + sync request, a vote is no longer recorded before it is validated, and a + failed BLS vote-signature check is logged under the governance category + instead of unconditionally, so a peer cannot fill `debug.log` with it. +- Socket exception monitoring is now consistent between the `poll()` and + `select()` backends, and a peer whose send buffer stalls is now subject to the + send timeout, so it can no longer hold a connection slot indefinitely. +- Verified inbound connections are bounded per operator identity; `CHECKDATASIG` + uses the signature cache; repeated authenticated Platform bans are skipped; + `DYNBITSET` allocation is bounded against the remaining stream size; a bogus + InstantSend lock no longer drops a batch of unrelated locks; and + `-allowprivatenet` no longer influences masternode address validation. + +## Wallet + +- Invalid-mnemonic errors no longer echo the recovery phrase, an encrypted + wallet is relocked if an HD upgrade fails after it was unlocked, and a wallet + unlock deadline can no longer be extended without re-authenticating. + +# v23.1.9 Change log See detailed [set of changes][set-of-changes]. @@ -139,6 +134,7 @@ debug the release candidates. These releases are considered obsolete. Old release notes can be found here: +- [v23.1.8](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-23.1.8.md) released Jul/29/2026 - [v23.1.7](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-23.1.7.md) released Jun/30/2026 - [v23.1.5](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-23.1.5.md) released Jun/19/2026 - [v23.1.4](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-23.1.4.md) released Jun/18/2026 @@ -158,4 +154,4 @@ These releases are considered obsolete. Old release notes can be found here: - [v21.0.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-21.0.0.md) released Jul/25/2024 - [v20.1.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-20.1.1.md) released April/3/2024 -[set-of-changes]: https://github.com/dashpay/dash/compare/v23.1.7...dashpay:v23.1.8 +[set-of-changes]: https://github.com/dashpay/dash/compare/v23.1.8...dashpay:v23.1.9 diff --git a/doc/release-notes/dash/release-notes-23.1.8.md b/doc/release-notes/dash/release-notes-23.1.8.md new file mode 100644 index 000000000000..2a8d804abe00 --- /dev/null +++ b/doc/release-notes/dash/release-notes-23.1.8.md @@ -0,0 +1,161 @@ +# Dash Core version v23.1.8 + +This is a new patch version release, fixing three remotely reachable crashes and +bringing further hardening of the peer-to-peer message handlers along with +networking, RPC and build fixes. +Upgrading is **strongly recommended** for all nodes, and required for +masternodes. + +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). + +## Downgrade warning + +### Downgrade to a version < v23.0.0 + +Downgrading to a version older than v23.0.0 is not supported, and will +require a reindex. + +# Release Notes + +## Critical fixes + +This release fixes three crashes that a remote party could trigger. None of +them affect consensus rules or put funds at risk, but each one can take a node +offline, so all operators should upgrade promptly. + +- Fixed a crash while removing provider transactions that a masternode's + operator-key change invalidates. Those transactions are collected before any + of them are removed, so when one was an in-mempool descendant of another it + was already erased along with its ancestor, and the stale entry was then + dereferenced. Such entries are now skipped. This is reachable whenever a block + carries a provider registrar update or revocation for a masternode that has + chained service updates pending in the mempool. +- Fixed a crash caused by an unvalidated LLMQ type in a `qsigshare` message. A + masternode that received a signature share naming an LLMQ type its chain does + not register would index a per-type quorum cache that is only populated for + known types, aborting the process. Unregistered types are now rejected before + the lookup, and the affected cache lookups no longer create missing entries. +- Fixed a crash caused by a quorum commitment naming a block with no parent, + such as the genesis block. The parentless block index reached a non-null + precondition and terminated the process instead of failing validation, which + no exception handler could contain. Commitments with a parentless quorum base + block are now rejected, and the LLMQ activation check treats a null + predecessor as "not enabled" rather than a contract violation. + +## Security + +This release continues the hardening of peer-to-peer message handlers against +denial-of-service from remote peers. These issues do not affect consensus and do +not put funds at risk, but they could be used to crash or degrade nodes - +masternodes in particular - so upgrading is recommended. + +- LLMQ / signing: the queues of not-yet-verified recovered signatures and + signature shares are now bounded, and the vectors carried by the QSIGSHARE, + QSIGSESANN, QSIGSHARESINV, QGETSIGSHARES and QBSIGSHARES messages are bounded + before any allocation or decoding takes place. The number of signing share + sessions a single peer may announce is also capped, so a peer can no longer + grow that per-peer state without limit (dash#7351). +- LLMQ / DKG: the number of encrypted contribution blobs in a DKG contribution + is now checked against the quorum's lower bound as well as its upper bound. +- LLMQ / quorum data: the verification vector and encrypted contribution + vectors in QDATA responses are validated against their expected sizes before + any BLS decoding is performed. +- Transaction relay: an oversized `notfound` message is now penalised rather + than silently ignored (dash#7348). +- ChainLocks: the cache of seen ChainLock signatures is now bounded. +- Governance: per-object vote sync requests are now throttled per peer, and + governance object and vote responses are only accepted from a peer if that + peer announced them or they were requested from it, using the net-layer + per-peer request tracker. Governance vote signatures are bounded when read + from the network and must use one of the two legitimate encodings. +- CoinJoin: the vectors carried by CoinJoin mixing messages are bounded before + allocation, and a non-participant can no longer abort another session's + signing phase. An invalid `dstx` message now carries a misbehaviour score + instead of being dropped for free (dash#7347). +- Bloom filters: filterload and filteradd payloads are bounded before + allocation. +- Sporks: spork signatures are bounded during deserialization, and malformed + spork messages now attribute misbehaviour to the sending peer. +- Compact block relay: batched hardening backported from upstream Bitcoin Core + (dash#7398), including detection of mutated blocks as a defence-in-depth + measure. + +## RPC + +- `protx listdiff` no longer reports an always-zero `platformP2PPort` / + `platformHTTPPort` for masternodes registered with extended addresses; the + live Platform ports are reported instead. + +## GUI + +- The PoSe score column is no longer hidden together with banned masternodes in + the masternode list. +- Fixed an abort when scaling widgets whose font was set in pixels rather than + points (for example by a stylesheet's `font-size: Npx`); such fonts are now + converted to a point size instead of being assumed to have one (dash#7465). + +## Build and CI + +- Fixed a CMake compatibility error when building the freetype dependency with + newer CMake (dash#7372). +- Stabilized the `-par` / `-parbls` help text (and the generated man pages) so + they no longer embed the core count of the build machine. +- Updated GitHub Actions pins for the Node 24 runtime. +- Fixed the circular-dependencies lint script under Python 3.15. + +## Tests + +- Governance inventory cache coverage moved from a functional test to unit + tests, and governance vote test fixtures are now wire-valid. + +# v23.1.8 Change log + +See detailed [set of changes][set-of-changes]. + +# Credits + +Thanks to everyone who directly contributed to this release: + +- Konstantin Akimov +- PastaClaw +- PastaPastaPasta +- UdjinM6 + +As well as everyone that submitted issues, reviewed pull requests and helped +debug the release candidates. + +# Older releases + +These releases are considered obsolete. Old release notes can be found here: + +- [v23.1.7](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-23.1.7.md) released Jun/30/2026 +- [v23.1.5](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-23.1.5.md) released Jun/19/2026 +- [v23.1.4](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-23.1.4.md) released Jun/18/2026 +- [v23.1.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-23.1.3.md) released May/28/2026 +- [v23.1.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-23.1.2.md) released Mar/12/2026 +- [v23.1.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-23.1.0.md) released Feb/15/2026 +- [v23.0.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-23.0.2.md) released Dec/4/2025 +- [v23.0.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-23.0.0.md) released Nov/10/2025 +- [v22.1.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-22.1.3.md) released Jul/15/2025 +- [v22.1.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-22.1.2.md) released Apr/15/2025 +- [v22.1.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-22.1.1.md) released Feb/17/2025 +- [v22.1.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-22.1.0.md) released Feb/10/2025 +- [v22.0.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-22.0.0.md) released Dec/12/2024 +- [v21.1.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-21.1.1.md) released Oct/22/2024 +- [v21.1.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-21.1.0.md) released Aug/8/2024 +- [v21.0.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-21.0.2.md) released Aug/1/2024 +- [v21.0.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-21.0.0.md) released Jul/25/2024 +- [v20.1.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-20.1.1.md) released April/3/2024 + +[set-of-changes]: https://github.com/dashpay/dash/compare/v23.1.7...dashpay:v23.1.8 diff --git a/src/active/dkgsession.cpp b/src/active/dkgsession.cpp index f9866bfb2651..ecf83d6890e7 100644 --- a/src/active/dkgsession.cpp +++ b/src/active/dkgsession.cpp @@ -93,6 +93,14 @@ std::optional ActiveDKGSession::SendContributions() skContrib.MakeNewKey(); } + if (i != myIdx && ShouldSimulateError(DKGError::type::CONTRIBUTION_INVALID)) { + // Deliver a share that decrypts to an all-zero (structurally invalid) + // CBLSSecretKey on the recipient. Used to exercise the DKG intake's + // handling of a peer-supplied invalid contribution share. + logger.Batch("sending invalid (zero) share to %s", m->dmn->proTxHash.ToString()); + skContrib = CBLSSecretKey{}; + } + if (!qc.contributions->Encrypt(i, m->dmn->pdmnState->pubKeyOperator.Get(), skContrib, PROTOCOL_VERSION)) { logger.Batch("failed to encrypt contribution for %s", m->dmn->proTxHash.ToString()); return std::nullopt; diff --git a/src/bls/bls_worker.cpp b/src/bls/bls_worker.cpp index eade0642bc44..aeae68b1ed3b 100644 --- a/src/bls/bls_worker.cpp +++ b/src/bls/bls_worker.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -299,8 +300,14 @@ struct Aggregator : public std::enable_shared_from_this> { template static T SyncAggregate(Span vec, size_t start, size_t count) { + // Defence in depth: AggregateInsecure() asserts that every operand is valid, so + // peer-influenced data (e.g. a decrypted contribution share) must never reach it + // while invalid. Short-circuit to an invalid aggregate instead; callers already + // treat an invalid result as an aggregation failure rather than aborting. T result = *vec[start]; + if (!result.IsValid()) return {}; for (size_t j = 1; j < count; j++) { + if (!vec[start + j]->IsValid()) return {}; result.AggregateInsecure(*vec[start + j]); } return result; @@ -681,6 +688,13 @@ CBLSPublicKey CBLSWorker::BuildPubKeyShare(const BLSVerificationVectorPtr& vvec, void CBLSWorker::AsyncVerifyContributionShares(const CBLSId& forId, Span vvecs, Span skShares, bool parallel, bool aggregated, std::function&)> doneCallback) { + if (!forId.IsValid()) { + std::vector result; + result.assign(vvecs.size(), false); + doneCallback(result); + return; + } + // Empty input never schedules any work, so doneCallback would never fire and the // future-based wrappers would throw broken_promise. Mirror the empty-input // short-circuit in AsyncBuildQuorumVerificationVector / AsyncAggregateHelper. @@ -688,9 +702,31 @@ void CBLSWorker::AsyncVerifyContributionShares(const CBLSId& forId, Span{}); return; } - if (!forId.IsValid() || !VerifyVerificationVectors(vvecs)) { + + if (!VerifyVerificationVectors(vvecs)) { + // A batch-level verification-vector problem must not fail the whole batch. The + // only thing the batched VerifyVerificationVectors adds over the per-member + // VerifyVerificationVector check already run in PreVerifyMessage is detecting a + // coefficient point shared between two members' vvecs. Failing the whole batch on + // that lets one member, by placing a coefficient that collides with another + // member's, force every other honest member in the same verification batch to be + // marked invalid and complained about - a denial of service against their + // participation in the DKG. Instead, verify each contribution on its own: a member + // fails only if its own share is inconsistent with its own vvec, so a member that + // placed a colliding coefficient is caught on its own while the honest members it + // collided with verify normally. + LogPrint(BCLog::LLMQ_DKG, "CBLSWorker::%s -- batch verification-vector check failed; verifying %d contributions individually\n", + __func__, vvecs.size()); + std::vector> futures; + futures.reserve(vvecs.size()); + for (size_t i = 0; i < vvecs.size(); i++) { + futures.emplace_back(AsyncVerifyContributionShare(forId, vvecs[i], skShares[i])); + } std::vector result; - result.assign(vvecs.size(), false); + result.reserve(vvecs.size()); + for (auto& f : futures) { + result.push_back(f.get()); + } doneCallback(result); return; } diff --git a/src/chain.h b/src/chain.h index d3ea67d24dfa..a67a88641e9f 100644 --- a/src/chain.h +++ b/src/chain.h @@ -41,8 +41,8 @@ class CBlockFileInfo { public: unsigned int nBlocks{}; //!< number of blocks stored in file - unsigned int nSize{}; //!< number of used bytes of block file - unsigned int nUndoSize{}; //!< number of used bytes in the undo file + uint64_t nSize{}; //!< number of used bytes of block file + uint64_t nUndoSize{}; //!< number of used bytes in the undo file unsigned int nHeightFirst{}; //!< lowest height of block in file unsigned int nHeightLast{}; //!< highest height of block in file uint64_t nTimeFirst{}; //!< earliest time of block in file @@ -158,10 +158,10 @@ class CBlockIndex int nFile GUARDED_BY(::cs_main){0}; //! Byte offset within blk?????.dat where this block's data is stored - unsigned int nDataPos GUARDED_BY(::cs_main){0}; + uint64_t nDataPos GUARDED_BY(::cs_main){0}; //! Byte offset within rev?????.dat where this block's undo data is stored - unsigned int nUndoPos GUARDED_BY(::cs_main){0}; + uint64_t nUndoPos GUARDED_BY(::cs_main){0}; //! (memory only) Total amount of work (expected number of hashes) in the chain up to and including this block arith_uint256 nChainWork{}; diff --git a/src/flatfile.cpp b/src/flatfile.cpp index ef8df497afaf..bee9e6d9b51c 100644 --- a/src/flatfile.cpp +++ b/src/flatfile.cpp @@ -45,7 +45,7 @@ FILE* FlatFileSeq::Open(const FlatFilePos& pos, bool read_only) LogPrintf("Unable to open file %s\n", fs::PathToString(path)); return nullptr; } - if (pos.nPos && fseek(file, pos.nPos, SEEK_SET)) { + if (pos.nPos && FileSeek64(file, pos.nPos, SEEK_SET)) { LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, fs::PathToString(path)); fclose(file); return nullptr; @@ -57,11 +57,11 @@ size_t FlatFileSeq::Allocate(const FlatFilePos& pos, size_t add_size, bool& out_ { out_of_space = false; - unsigned int n_old_chunks = (pos.nPos + m_chunk_size - 1) / m_chunk_size; - unsigned int n_new_chunks = (pos.nPos + add_size + m_chunk_size - 1) / m_chunk_size; + uint64_t n_old_chunks = (pos.nPos + m_chunk_size - 1) / m_chunk_size; + uint64_t n_new_chunks = (pos.nPos + add_size + m_chunk_size - 1) / m_chunk_size; if (n_new_chunks > n_old_chunks) { - size_t old_size = pos.nPos; - size_t new_size = n_new_chunks * m_chunk_size; + uint64_t old_size = pos.nPos; + uint64_t new_size = n_new_chunks * m_chunk_size; size_t inc_size = new_size - old_size; if (CheckDiskSpace(m_dir, inc_size)) { diff --git a/src/flatfile.h b/src/flatfile.h index 44b7c5ef9f51..d35793a26e93 100644 --- a/src/flatfile.h +++ b/src/flatfile.h @@ -14,7 +14,7 @@ struct FlatFilePos { int nFile{-1}; - unsigned int nPos{0}; + uint64_t nPos{0}; SERIALIZE_METHODS(FlatFilePos, obj) { @@ -23,7 +23,7 @@ struct FlatFilePos FlatFilePos() {} - FlatFilePos(int nFileIn, unsigned int nPosIn) : + FlatFilePos(int nFileIn, uint64_t nPosIn) : nFile(nFileIn), nPos(nPosIn) {} diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index e4d792f122b4..fb4f26ddf1e9 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -388,6 +389,14 @@ void CGovernanceManager::AddGovernanceObjectForTesting(const CGovernanceObject& mapObjects.emplace(govobj.GetHash(), std::make_shared(govobj)); } +void CGovernanceManager::AddOrphanVoteForTesting(const CGovernanceVote& vote) +{ + AssertLockNotHeld(cs_store); + LOCK(cs_store); + cmmapOrphanVotes.Insert(vote.GetParentHash(), + governance::OrphanVote{vote, Now() + GOVERNANCE_ORPHAN_EXPIRATION_TIME}); +} + void CGovernanceManager::CheckAndRemove() { AssertLockNotHeld(cs_store); @@ -1149,6 +1158,12 @@ std::vector CGovernanceManager::GetOrphanVoteObjectHashes() } } + // A random subset, so that parent hashes ground to sort first cannot pin which ones are requested + if (vecHashesFiltered.size() > MAX_ORPHAN_OBJECT_REQUESTS) { + Shuffle(vecHashesFiltered.begin(), vecHashesFiltered.end(), FastRandomContext()); + vecHashesFiltered.resize(MAX_ORPHAN_OBJECT_REQUESTS); + } + return vecHashesFiltered; } diff --git a/src/governance/governance.h b/src/governance/governance.h index 7d858930e967..631b2d939086 100644 --- a/src/governance/governance.h +++ b/src/governance/governance.h @@ -329,6 +329,8 @@ class CGovernanceManager : public GovernanceStore * running collateral or chain validation. */ void AddGovernanceObjectForTesting(const CGovernanceObject& govobj) EXCLUSIVE_LOCKS_REQUIRED(!cs_store); + /** Test-only helper: caches a vote as an orphan of its parent without validating it. */ + void AddOrphanVoteForTesting(const CGovernanceVote& vote) EXCLUSIVE_LOCKS_REQUIRED(!cs_store); // Thread-safe accessors bool HaveObjectForHash(const uint256& nHash) const @@ -355,7 +357,12 @@ class CGovernanceManager : public GovernanceStore // Used by NetGovernance std::vector FetchRelayInventory() EXCLUSIVE_LOCKS_REQUIRED(!cs_relay); void CheckAndRemove() EXCLUSIVE_LOCKS_REQUIRED(!cs_store); - /** Get hashes of governance objects for which we have orphan votes. Also cleans up expired orphans. */ + /** Upper bound on the orphan parent hashes GetOrphanVoteObjectHashes() returns per call. Each one is + * re-requested from every peer, and one masternode key can sign votes for any number of invented + * parent hashes. */ + static constexpr size_t MAX_ORPHAN_OBJECT_REQUESTS{100}; + /** Get hashes of governance objects for which we have orphan votes, at most + * MAX_ORPHAN_OBJECT_REQUESTS of them chosen at random. Also cleans up expired orphans. */ [[nodiscard]] std::vector GetOrphanVoteObjectHashes() EXCLUSIVE_LOCKS_REQUIRED(!cs_store); std::pair, std::vector> FetchGovernanceObjectVotes( size_t peers_per_hash_max, int64_t now, std::map>& map_asked_recently) const diff --git a/src/index/blockfilterindex.cpp b/src/index/blockfilterindex.cpp index 26857b0f0f34..2ffec43aaab8 100644 --- a/src/index/blockfilterindex.cpp +++ b/src/index/blockfilterindex.cpp @@ -493,7 +493,7 @@ bool BlockFilterIndex::LookupFilterRange(int start_height, const CBlockIndex* st if (!file || entry.pos.nFile != file_num) { file = std::make_unique(m_filter_fileseq->Open(entry.pos, true)); file_num = entry.pos.nFile; - } else if (fseek(file->Get(), entry.pos.nPos, SEEK_SET) != 0) { + } else if (FileSeek64(file->Get(), entry.pos.nPos, SEEK_SET) != 0) { LogError("%s: unable to seek to position %u in filter file %d\n", __func__, entry.pos.nPos, entry.pos.nFile); return false; diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp index 7cb5069f43e1..000b8ebe2cc7 100644 --- a/src/index/txindex.cpp +++ b/src/index/txindex.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -87,7 +88,7 @@ bool TxIndex::FindTx(const uint256& tx_hash, uint256& block_hash, CTransactionRe CBlockHeader header; try { file >> header; - if (fseek(file.Get(), postx.nTxOffset, SEEK_CUR)) { + if (FileSeek64(file.Get(), postx.nTxOffset, SEEK_CUR)) { LogError("%s: fseek(...) failed\n", __func__); return false; } diff --git a/src/llmq/dkgsession.cpp b/src/llmq/dkgsession.cpp index e7a1998f8a4c..0df95d9fbd49 100644 --- a/src/llmq/dkgsession.cpp +++ b/src/llmq/dkgsession.cpp @@ -238,6 +238,13 @@ std::optional CDKGSession::ReceiveMessage(const CDKGContribution& qc) if (!MaybeDecrypt(*qc.contributions, *myIdx, skContribution, PROTOCOL_VERSION)) { logger.Batch("contribution from %s could not be decrypted", member->dmn->proTxHash.ToString()); complain = true; + } else if (!skContribution.IsValid()) { + // A decryptable but structurally-invalid (e.g. all-zero) share must never be + // retained: it later reaches CBLSSecretKey::AggregateInsecure() in the batched + // contribution verification, whose assert(IsValid()) would abort an honest node. + // Treat it as a bad contribution and complain. + logger.Batch("contribution from %s decrypted to an invalid share", member->dmn->proTxHash.ToString()); + complain = true; } else if (member->idx != myIdx && ShouldSimulateError(DKGError::type::COMPLAIN_LIE)) { logger.Batch("lying/complaining for %s", member->dmn->proTxHash.ToString()); complain = true; diff --git a/src/llmq/dkgsession.h b/src/llmq/dkgsession.h index 91b77abf1cf2..6b29c3427b0e 100644 --- a/src/llmq/dkgsession.h +++ b/src/llmq/dkgsession.h @@ -69,6 +69,7 @@ class DKGError { CONTRIBUTION_LIE, JUSTIFY_OMIT, JUSTIFY_LIE, + CONTRIBUTION_INVALID, _COUNT }; static constexpr DKGError::type from_string(std::string_view in) { @@ -79,6 +80,7 @@ class DKGError { if (in == "contribution-lie") return CONTRIBUTION_LIE; if (in == "justify-lie") return JUSTIFY_LIE; if (in == "justify-omit") return JUSTIFY_OMIT; + if (in == "contribution-invalid") return CONTRIBUTION_INVALID; return _COUNT; } }; diff --git a/src/llmq/net_quorum.cpp b/src/llmq/net_quorum.cpp index 193e959f91f1..ef58ef006ea1 100644 --- a/src/llmq/net_quorum.cpp +++ b/src/llmq/net_quorum.cpp @@ -336,8 +336,11 @@ bool NetQuorum::ProcessContribQDATA(const CNode& pfrom, CDataStream& vRecv, std::vector vecSecretKeys; vecSecretKeys.resize(vecEncrypted.size()); for (const auto i : util::irange(vecEncrypted.size())) { - if (!m_nodeman->Decrypt(vecEncrypted[i], memberIdx, vecSecretKeys[i], PROTOCOL_VERSION)) { - m_peer_manager->PeerMisbehaving(pfrom.GetId(), 10, "failed to decrypt"); + if (!m_nodeman->Decrypt(vecEncrypted[i], memberIdx, vecSecretKeys[i], PROTOCOL_VERSION) || + !vecSecretKeys[i].IsValid()) { + // Reject an undecryptable or structurally-invalid (e.g. all-zero) share before + // it reaches AggregateSecretKeys()/AggregateInsecure(), which asserts validity. + m_peer_manager->PeerMisbehaving(pfrom.GetId(), 10, "invalid contribution share"); return false; } } diff --git a/src/llmq/net_signing.cpp b/src/llmq/net_signing.cpp index c89e87dac140..2deef488632e 100644 --- a/src/llmq/net_signing.cpp +++ b/src/llmq/net_signing.cpp @@ -400,7 +400,7 @@ void NetSigning::ProcessPendingSigShares( { // It's ok to perform insecure batched verification here as we verify against the quorum public key shares, // which are not craftable by individual entities, making the rogue public key attack impossible - CBLSBatchVerifier batchVerifier(false, true); + SigShareBatchVerifier batchVerifier(false, true); cxxtimer::Timer prepareTimer(true); size_t verifyCount = 0; @@ -431,7 +431,7 @@ void NetSigning::ProcessPendingSigShares( assert(false); } - batchVerifier.PushMessage(nodeId, sigShare.GetKey(), sigShare.GetSignHash(), sig, pubKeyShare); + PushSigShareToBatchVerifier(batchVerifier, nodeId, sigShare, sig, pubKeyShare); verifyCount++; } } diff --git a/src/llmq/signing_shares.cpp b/src/llmq/signing_shares.cpp index 45fbc9ee2fdf..5dd6ce91a3bc 100644 --- a/src/llmq/signing_shares.cpp +++ b/src/llmq/signing_shares.cpp @@ -48,6 +48,12 @@ void CSigShare::UpdateKey() key.second = quorumMember; } +void PushSigShareToBatchVerifier(SigShareBatchVerifier& batchVerifier, NodeId from, const CSigShare& sigShare, + const CBLSSignature& sig, const CBLSPublicKey& pubKeyShare) +{ + batchVerifier.PushMessage(from, {from, sigShare.GetKey()}, sigShare.GetSignHash(), sig, pubKeyShare); +} + std::string CSigSesAnn::ToString() const { return strprintf("sessionId=%d, llmqType=%d, quorumHash=%s, id=%s, msgHash=%s", diff --git a/src/llmq/signing_shares.h b/src/llmq/signing_shares.h index 4a8326d522ff..52a907df8f17 100644 --- a/src/llmq/signing_shares.h +++ b/src/llmq/signing_shares.h @@ -6,6 +6,7 @@ #define BITCOIN_LLMQ_SIGNING_SHARES_H #include +#include #include #include #include @@ -88,6 +89,14 @@ class CSigShare : virtual public CSigBase } }; +// Batch entries are keyed by sender as well as SigShareKey. QSIGSHARE is accepted from any peer, so two peers can +// push the same key; with a sender-less key the second push would alias the first entry, letting one peer's share be +// judged in place of the other's (banning an honest sender, or never checking a forged share). +using SigShareBatchVerifier = CBLSBatchVerifier>; + +void PushSigShareToBatchVerifier(SigShareBatchVerifier& batchVerifier, NodeId from, const CSigShare& sigShare, + const CBLSSignature& sig, const CBLSPublicKey& pubKeyShare); + // Nodes will first announce a signing session with a sessionId to be used in all future P2P messages related to that // session. We locally keep track of the mapping for each node. We also assign new sessionIds for outgoing sessions // and send QSIGSESANN messages appropriately. All values except the max value for uint32_t are valid as sessionId diff --git a/src/net.cpp b/src/net.cpp index 30fd489d82f0..b8f6a57e7459 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1763,7 +1763,11 @@ std::pair CConnman::SocketSendData(CNode& node) const } } - node.fPauseSend = node.m_send_memusage + node.m_transport->GetSendMemoryUsage() > nSendBufferMaxSize; + const bool pause_send{node.m_send_memusage + node.m_transport->GetSendMemoryUsage() > nSendBufferMaxSize}; + if (pause_send && !node.fPauseSend) { + node.m_pause_send_since = GetTime(); + } + node.fPauseSend = pause_send; if (it == node.vSendMsg.end()) { assert(node.m_send_memusage == 0); @@ -2321,6 +2325,14 @@ bool CConnman::InactivityCheck(const CNode& node) const return true; } + if (node.fPauseSend) { + const auto last_pause{node.m_pause_send_since.load()}; + if (now > last_pause + TIMEOUT_INTERVAL) { + LogPrint(BCLog::NET, "send buffer full for %is peer=%d\n", count_seconds(now - last_pause), node.GetId()); + return true; + } + } + if (!node.fSuccessfullyConnected) { if (node.m_transport->GetInfo().transport_type == TransportProtocolType::DETECTING) { LogPrint(BCLog::NET, "V2 handshake timeout peer=%d\n", node.GetId()); @@ -2342,7 +2354,7 @@ Sock::EventsPerSock CConnman::GenerateWaitSockets(Span nodes) } for (CNode* pnode : nodes) { - bool select_recv = !pnode->fHasRecvData; + bool select_recv = !pnode->fHasRecvData && !pnode->fPauseRecv; bool select_send = !pnode->fCanSendData; if (!select_recv && !select_send) continue; @@ -4886,7 +4898,10 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg) // Update memory usage of send buffer. pnode->m_send_memusage += msg.GetMemoryUsage(); - if (pnode->m_send_memusage + pnode->m_transport->GetSendMemoryUsage() > nSendBufferMaxSize) pnode->fPauseSend = true; + if (pnode->m_send_memusage + pnode->m_transport->GetSendMemoryUsage() > nSendBufferMaxSize && !pnode->fPauseSend) { + pnode->m_pause_send_since = GetTime(); + pnode->fPauseSend = true; + } // Move message to vSendMsg queue. pnode->vSendMsg.push_back(std::move(msg)); pnode->nSendMsgSize = pnode->vSendMsg.size(); diff --git a/src/net.h b/src/net.h index 3b1ded7de0fb..c23545887849 100644 --- a/src/net.h +++ b/src/net.h @@ -828,6 +828,8 @@ class CNode std::atomic_bool fPauseRecv{false}; std::atomic_bool fPauseSend{false}; + /** When was fPauseSend last set from false to true */ + std::atomic m_pause_send_since{0s}; std::atomic_bool fHasRecvData{false}; std::atomic_bool fCanSendData{false}; diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp index 822e983573cb..896f03e7fa93 100644 --- a/src/node/blockstorage.cpp +++ b/src/node/blockstorage.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -522,12 +523,12 @@ static bool UndoWriteToDisk(const CBlockUndo& blockundo, FlatFilePos& pos, const fileout << messageStart << nSize; // Write undo data - long fileOutPos = ftell(fileout.Get()); + const int64_t fileOutPos{FileTell64(fileout.Get())}; if (fileOutPos < 0) { LogError("%s: ftell failed\n", __func__); return false; } - pos.nPos = (unsigned int)fileOutPos; + pos.nPos = fileOutPos; fileout << blockundo; // calculate & write checksum @@ -782,12 +783,12 @@ static bool WriteBlockToDisk(const CBlock& block, FlatFilePos& pos, const CMessa fileout << messageStart << nSize; // Write block - long fileOutPos = ftell(fileout.Get()); + const int64_t fileOutPos{FileTell64(fileout.Get())}; if (fileOutPos < 0) { LogError("WriteBlockToDisk: ftell failed\n"); return false; } - pos.nPos = (unsigned int)fileOutPos; + pos.nPos = fileOutPos; fileout << block; return true; diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 14ae6d160349..f289c75cefaf 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -232,6 +232,13 @@ inline CAmount CalculateOutputValue(const TxType& tx) } +/** nVersion and nType share one 32-bit wire field. nVersion fills the low half as raw bits: + * sign-extending a negative nVersion would overwrite nType. */ +constexpr int32_t PackVersionAndType(int16_t nVersion, uint16_t nType) +{ + return static_cast(static_cast(nVersion) | (static_cast(nType) << 16)); +} + /** The basic transaction that is broadcasted on the network and contained in * blocks. A transaction can contain multiple inputs and outputs. */ @@ -268,7 +275,7 @@ class CTransaction template inline void Serialize(Stream& s) const { - int32_t n32bitVersion = this->nVersion | (this->nType << 16); + int32_t n32bitVersion = PackVersionAndType(this->nVersion, this->nType); s << n32bitVersion; s << vin; s << vout; @@ -354,7 +361,7 @@ struct CMutableTransaction SERIALIZE_METHODS(CMutableTransaction, obj) { int32_t n32bitVersion; - SER_WRITE(obj, n32bitVersion = obj.nVersion | (obj.nType << 16)); + SER_WRITE(obj, n32bitVersion = PackVersionAndType(obj.nVersion, obj.nType)); READWRITE(n32bitVersion); SER_READ(obj, obj.nVersion = (int16_t) (n32bitVersion & 0xffff)); SER_READ(obj, obj.nType = (uint16_t) ((n32bitVersion >> 16) & 0xffff)); diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index cc5b7c562b59..f997090edd62 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1416,7 +1416,7 @@ class CTransactionSignatureSerializer template void Serialize(S &s) const { // Serialize nVersion - int32_t n32bitVersion = txTo.nVersion | (txTo.nType << 16); + int32_t n32bitVersion = PackVersionAndType(txTo.nVersion, txTo.nType); ::Serialize(s, n32bitVersion); // Serialize vin unsigned int nInputs = fAnyoneCanPay ? 1 : txTo.vin.size(); diff --git a/src/test/bls_tests.cpp b/src/test/bls_tests.cpp index 7ae4c871818a..49a6940598a0 100644 --- a/src/test/bls_tests.cpp +++ b/src/test/bls_tests.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include