diff --git a/.github/workflows/label-merge-conflicts.yml b/.github/workflows/label-merge-conflicts.yml index c6d4ad742d00..b804b36c5138 100644 --- a/.github/workflows/label-merge-conflicts.yml +++ b/.github/workflows/label-merge-conflicts.yml @@ -11,11 +11,12 @@ on: permissions: contents: read pull-requests: write + # issues: write is required so the action can manage labels and post comments on PRs + issues: write # Enforce other not needed permissions are off actions: none checks: none deployments: none - issues: none #metadata: read packages: none repository-projects: none @@ -27,7 +28,7 @@ jobs: runs-on: ubuntu-latest steps: - name: check if prs are dirty - uses: eps1lon/actions-label-merge-conflict@releases/2.x + uses: eps1lon/actions-label-merge-conflict@v3.1.0 with: dirtyLabel: "needs rebase" repoToken: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/merge-check.yml b/.github/workflows/merge-check.yml index 7fc2e22a8705..1a3029a8bbe3 100644 --- a/.github/workflows/merge-check.yml +++ b/.github/workflows/merge-check.yml @@ -1,7 +1,10 @@ name: Check Merge Fast-Forward Only permissions: + contents: read pull-requests: write + # Required so we can apply labels to PRs (labels go through the issues API) + issues: write on: push: @@ -42,11 +45,16 @@ jobs: fi - name: add labels - uses: actions-ecosystem/action-add-labels@v1 - if: failure() + uses: actions/github-script@v8 + if: failure() && github.event.pull_request with: - labels: | - needs rebase + script: | + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + labels: ['needs rebase'] + }); - name: comment uses: mshick/add-pr-comment@v2 diff --git a/.github/workflows/release_docker_hub.yml b/.github/workflows/release_docker_hub.yml index f4e1775a7859..fb465665cfdb 100644 --- a/.github/workflows/release_docker_hub.yml +++ b/.github/workflows/release_docker_hub.yml @@ -33,7 +33,7 @@ jobs: echo "build_tag=${TAG#v}" >> $GITHUB_OUTPUT - name: Set suffix - uses: actions/github-script@v6 + uses: actions/github-script@v8 id: suffix with: result-encoding: string diff --git a/.github/workflows/semantic-pull-request.yml b/.github/workflows/semantic-pull-request.yml index 3ad1d1a4d93d..191a34f37381 100644 --- a/.github/workflows/semantic-pull-request.yml +++ b/.github/workflows/semantic-pull-request.yml @@ -12,7 +12,7 @@ jobs: name: Validate PR title runs-on: ubuntu-latest steps: - - uses: amannn/action-semantic-pull-request@v5 + - uses: amannn/action-semantic-pull-request@v6 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/configure.ac b/configure.ac index 91beef9b689a..fb96e7d29cec 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ AC_PREREQ([2.69]) dnl Don't forget to push a corresponding tag when updating any of _CLIENT_VERSION_* numbers define(_CLIENT_VERSION_MAJOR, 23) define(_CLIENT_VERSION_MINOR, 1) -define(_CLIENT_VERSION_BUILD, 7) +define(_CLIENT_VERSION_BUILD, 8) define(_CLIENT_VERSION_IS_RELEASE, true) define(_COPYRIGHT_YEAR, 2026) define(_COPYRIGHT_HOLDERS,[The %s developers]) diff --git a/contrib/containers/ci/ci-slim.Dockerfile b/contrib/containers/ci/ci-slim.Dockerfile index 5332f8504a57..8436fd14dfa3 100644 --- a/contrib/containers/ci/ci-slim.Dockerfile +++ b/contrib/containers/ci/ci-slim.Dockerfile @@ -81,7 +81,6 @@ RUN uv pip install --system --break-system-packages \ flake8==5.0.4 \ jinja2 \ lief==0.13.2 \ - multiprocess \ mypy==0.981 \ pyzmq==24.0.1 \ vulture==2.6 diff --git a/contrib/containers/guix/scripts/setup-sdk b/contrib/containers/guix/scripts/setup-sdk index 9781ce07285d..6f00e57500b1 100755 --- a/contrib/containers/guix/scripts/setup-sdk +++ b/contrib/containers/guix/scripts/setup-sdk @@ -15,6 +15,35 @@ XCODE_RELEASE="${XCODE_RELEASE:-15A240d}" XCODE_ARCHIVE="Xcode-${XCODE_VERSION}-${XCODE_RELEASE}-extracted-SDK-with-libcxx-headers" XCODE_AR_PATH="${SDK_SOURCES}/${XCODE_ARCHIVE}.tar.gz" +# actions/upload-artifact rejects paths containing characters that are not +# portable across filesystems (notably ':'). The Xcode SDK ships thousands of +# Perl man pages under usr/share/man with '::' in the filename +# (e.g. APR::Base64.3pm). Compilation only needs headers/libs/tbd files; man +# pages are unused by depends/mac and guix darwin builds. Strip them after +# extract so CI can hand the prepared SDK tree to downstream jobs as an +# artifact (and so cache entries stay portable) without packaging changes in +# the pull_request_target caller workflows (which resolve from the default +# branch and cannot be fixed from a release-branch PR head alone). +sanitize_sdk_for_artifacts() { + local sdk_root="$1" + local man_dir="${sdk_root}/usr/share/man" + + if [ -d "${man_dir}" ]; then + echo "Removing SDK man pages incompatible with actions/upload-artifact..." + rm -rf "${man_dir}" + fi + + # Belt-and-suspenders: drop any remaining files whose names contain the + # characters upload-artifact rejects, in case a future SDK adds more. + # See: https://github.com/actions/upload-artifact/issues/546 + if [ -d "${sdk_root}" ]; then + find "${sdk_root}" \( -type f -o -type l \) \ + \( -name '*:*' -o -name '*"*' -o -name '*<*' -o -name '*>*' \ + -o -name '*|*' -o -name '*\**' -o -name '*\?*' \) \ + -print -delete 2>/dev/null || true + fi +} + if [ ! -d "${SDK_PATH}/${XCODE_ARCHIVE}" ]; then if [ ! -f "${XCODE_AR_PATH}" ]; then echo "Downloading macOS SDK..." @@ -24,4 +53,9 @@ if [ ! -d "${SDK_PATH}/${XCODE_ARCHIVE}" ]; then echo "Extracting macOS SDK..." mkdir -p "${SDK_PATH}" tar -C "${SDK_PATH}" -xf "${XCODE_AR_PATH}" + sanitize_sdk_for_artifacts "${SDK_PATH}/${XCODE_ARCHIVE}" +else + # Existing tree (e.g. restored from an older cache that still has man + # pages): ensure it is safe to re-upload if a later step packages it. + sanitize_sdk_for_artifacts "${SDK_PATH}/${XCODE_ARCHIVE}" fi diff --git a/contrib/debian/examples/dash.conf b/contrib/debian/examples/dash.conf index 22965bc6c000..088f192622d3 100644 --- a/contrib/debian/examples/dash.conf +++ b/contrib/debian/examples/dash.conf @@ -1 +1,979 @@ -# This is a placeholder file. Please follow the instructions in `contrib/devtools/README.md` to generate a dash.conf file. +## +## dash.conf configuration file. +## Generated by contrib/devtools/gen-dash-conf.sh. +## +## Lines beginning with # are comments. +## All possible configuration options are provided. To use, copy this file +## to your data directory (default or specified by -datadir), uncomment +## options you would like to change, and save the file. +## + + +### Options + + +# Execute command when an alert is raised (%s in cmd is replaced by +# message) +#alertnotify= + +# If this block is in the chain assume that it and its ancestors are valid +# and potentially skip their script verification (0 to verify all, +# default: +# 00000000000000119fe42827219e0686d3f7b494ae65f823194c740c5dbab492, +# testnet: +# 000000541a23f9db7411cddbe50f9f1ebd4aa7108ebdcad62214753f648c0239, +# devnet: +# 0000000000000000000000000000000000000000000000000000000000000000) +#assumevalid= + +# Maintain an index of compact filters by block (default: 0, values: +# basic). If is not supplied or if = 1, indexes for +# all known types are enabled. Automatically enabled for +# masternodes with value 'basic'. +#blockfilterindex= + +# Execute command when the best block changes (%s in cmd is replaced by +# block hash) +#blocknotify= + +# Extra transactions to keep in memory for compact block reconstructions +# (default: 100) +#blockreconstructionextratxn= + +# Specify directory to hold blocks subdirectory for *.dat files (default: +# ) +#blocksdir= + +# Whether to reject transactions from network peers. Automatic broadcast +# and rebroadcast of any transactions from inbound peers is +# disabled, unless the peer has the 'forcerelay' permission. RPC +# transactions are not affected. (default: 0) +#blocksonly=1 + +# Execute command when the best chainlock changes (%s in cmd is replaced +# by chainlocked block hash) +#chainlocknotify= + +# Maintain coinstats index used by the gettxoutsetinfo RPC (default: 0) +#coinstatsindex=1 + +# Specify path to read-only configuration file. Relative paths will be +# prefixed by datadir location (only useable from command line, not +# configuration file) (default: dash.conf) +#conf= + +# Run in the background as a daemon and accept commands (default: 0) +#daemon=1 + +# Wait for initialization to be finished before exiting. This implies +# -daemon (default: 0) +#daemonwait=1 + +# Specify data directory +#datadir= + +# Maximum database cache size MiB (4 to 16384, default: 300). In +# addition, unused mempool memory is shared for this cache (see +# -maxmempool). +#dbcache= + +# Specify location of debug log file. Relative paths will be prefixed by a +# net-specific datadir location. (-nodebuglogfile to disable; +# default: debug.log) +#debuglogfile= + +# Specify additional configuration file, relative to the -datadir path +# (only useable from configuration file, not command line) +#includeconf= + +# Imports blocks from external file on startup +#loadblock= + +# Keep the transaction memory pool below megabytes (default: 300) +#maxmempool= + +# Maximum total size of all orphan transactions in megabytes (default: 10) +#maxorphantxsize= + +# Number of seconds to keep LLMQ recovery sigs (default: 604800) +#maxrecsigsage= + +# Do not keep transactions in the mempool longer than hours (default: +# 336) +#mempoolexpiry= + +# Set the number of script verification threads (0 = auto, <0 = leave that +# many cores free, max: 15, default: 0) +#par= + +# Set the number of BLS verification threads (0 = auto, <0 = leave that +# many cores free, max: 33, default: 0) +#parbls= + +# Whether to save the mempool on shutdown and load on restart (default: 1) +#persistmempool=1 + +# Specify pid file. Relative paths will be prefixed by a net-specific +# datadir location. (default: dashd.pid) +#pid= + +# Reduce storage requirements by enabling pruning (deleting) of old +# blocks. This allows the pruneblockchain RPC to be called to +# delete specific blocks, and enables automatic pruning of old +# blocks if a target size in MiB is provided. This mode is +# incompatible with -txindex, -rescan and -disablegovernance=false. +# Warning: Reverting this setting requires re-downloading the +# entire blockchain. (default: 0 = disable pruning blocks, 1 = +# allow manual pruning via RPC, >945 = automatically prune block +# files to stay under the specified target size in MiB) +#prune= + +# Specify path to dynamic settings data file. Can be disabled with +# -nosettings. File is written at runtime and not meant to be +# edited by users (use dash.conf instead for custom settings). +# Relative paths will be prefixed by datadir location. (default: +# settings.json) +#settings= + +# Execute command immediately before beginning shutdown. The need for +# shutdown may be urgent, so be careful not to delay it long (if +# the command doesn't require interaction with the server, consider +# having it fork into the background). +#shutdownnotify= + +# Execute command on startup. +#startupnotify= + +# Sync mempool from other nodes on start (default: 1) +#syncmempool=1 + +# Print version and exit +#version=1 + + +### Connection options + + +# Add a node to connect to and attempt to keep the connection open (see +# the addnode RPC help for more info). This option can be specified +# multiple times to add multiple nodes; connections are limited to +# 8 at a time and are counted separately from the -maxconnections +# limit. +#addnode= + +# Allow RFC1918 addresses to be relayed and connected to (default: 0) +#allowprivatenet=1 + +# Specify asn mapping used for bucketing of the peers (default: +# ip_asn.map). Relative paths will be prefixed by the net-specific +# datadir location. +#asmap= + +# Default duration (in seconds) of manually configured bans (default: +# 86400) +#bantime= + +# Bind to given address and always listen on it (default: 0.0.0.0). Use +# [host]:port notation for IPv6. Append =onion to tag any incoming +# connections to that address and port as incoming Tor connections +# (default: 127.0.0.1:9996=onion, testnet: 127.0.0.1:19996=onion, +# devnet: 127.0.0.1:19796=onion, regtest: 127.0.0.1:19896=onion) +#bind=[:][=onion] + +# If set, then this host is configured for CJDNS (connecting to fc00::/8 +# addresses would lead us to the CJDNS network, see doc/cjdns.md) +# (default: 0) +#cjdnsreachable=1 + +# Connect only to the specified node; -noconnect disables automatic +# connections (the rules for this peer are the same as for +# -addnode). This option can be specified multiple times to connect +# to multiple nodes. +#connect= + +# Discover own IP addresses (default: 1 when listening and no -externalip +# or -proxy) +#discover=1 + +# Allow DNS lookups for -addnode, -seednode and -connect (default: 1) +#dns=1 + +# Query for peer addresses via DNS lookup, if low on addresses (default: 1 +# unless -connect used or -maxconnections=0) +#dnsseed=1 + +# Specify your own public address +#externalip= + +# Allow fixed seeds if DNS seeds don't provide peers (default: 1) +#fixedseeds=1 + +# Always query for peer addresses via DNS lookup (default: 0) +#forcednsseed=1 + +# Whether to accept inbound I2P connections (default: 1). Ignored if +# -i2psam is not set. Listening for inbound I2P connections is done +# through the SAM proxy, not by binding to a local address and +# port. +#i2pacceptincoming=1 + +# I2P SAM proxy to reach I2P peers and accept I2P connections (default: +# none) +#i2psam= + +# Accept connections from outside (default: 1 if no -proxy, -connect or +# -maxconnections=0) +#listen=1 + +# Automatically create Tor onion service (default: 1) +#listenonion=1 + +# Maintain at most connections to peers (temporary service connections +# excluded) (default: 125). This limit does not apply to +# connections manually added via -addnode or the addnode RPC, which +# have a separate limit of 8. +#maxconnections= + +# Maximum per-connection receive buffer, *1000 bytes (default: 5000) +#maxreceivebuffer= + +# Maximum per-connection memory usage for the send buffer, *1000 bytes +# (default: 1000) +#maxsendbuffer= + +# Maximum allowed median peer time offset adjustment. Local perspective of +# time may be influenced by outbound peers forward or backward by +# this amount (default: 4200 seconds). +#maxtimeadjustment=1 + +# Tries to keep outbound traffic under the given target per 24h. Limit +# does not apply to peers with 'download' permission or blocks +# created within past week. 0 = no limit (default: 0M). Optional +# suffix units [k|K|m|M|g|G|t|T] (default: M). Lowercase is 1000 +# base while uppercase is 1024 base +#maxuploadtarget= + +# Use NAT-PMP to map the listening port (default: 0) +#natpmp=1 + +# Enable all P2P network activity (default: 1). Can be changed by the +# setnetworkactive RPC command +#networkactive=1 + +# Use separate SOCKS5 proxy to reach peers via Tor onion services, set +# -noonion to disable (default: -proxy). May be a local file path +# prefixed with 'unix:'. +#onion= + +# Make automatic outbound connections only to network (ipv4, ipv6, +# onion, i2p, cjdns). Inbound and manual connections are not +# affected by this option. It can be specified multiple times to +# allow multiple networks. +#onlynet= + +# Serve compact block filters to peers per BIP 157 (default: 0, +# automatically enabled for masternodes) +#peerblockfilters=1 + +# Support filtering of blocks and transaction with bloom filters (default: +# 1) +#peerbloomfilters=1 + +# Specify a p2p connection timeout delay in seconds. After connecting to a +# peer, wait this amount of time before considering disconnection +# based on inactivity (minimum: 1, default: 60) +#peertimeout= + +# Listen for connections on . Nodes not using the default ports +# (default: 9999, testnet: 19999, devnet: 19799, regtest: 19899) +# are unlikely to get incoming connections. Not relevant for I2P +# (see doc/i2p.md). +#port= + +# Connect through SOCKS5 proxy, set -noproxy to disable (default: +# disabled). May be a local file path prefixed with 'unix:' if the +# proxy supports it. +#proxy= + +# Randomize credentials for every proxy connection. This enables Tor +# stream isolation (default: 1) +#proxyrandomize=1 + +# Connect to a node to retrieve peer addresses, and disconnect. This +# option can be specified multiple times to connect to multiple +# nodes. +#seednode= + +# Socket events mode, which must be one of 'select', 'poll', 'epoll' or +# 'kqueue', depending on your system (default: Linux - 'epoll', +# FreeBSD/Apple - 'kqueue', Windows - 'select') +#socketevents= + +# Specify socket connection timeout in milliseconds. If an initial attempt +# to connect is unsuccessful after this amount of time, drop it +# (minimum: 1, default: 5000) +#timeout= + +# Tor control host and port to use if onion listening enabled (default: +# 127.0.0.1:9051). If no port is specified, the default port of +# 9051 will be used. +#torcontrol=: + +# Tor control port password (default: empty) +#torpassword= + +# Use UPnP to map the listening port (default: 0) +#upnp=1 + +# Support v2 transport (default: 1) +#v2transport=1 + +# Bind to the given address and add permission flags to the peers +# connecting to it. Use [host]:port notation for IPv6. Allowed +# permissions: bloomfilter (allow requesting BIP37 filtered blocks +# and transactions), noban (do not ban for misbehavior; implies +# download), forcerelay (relay transactions that are already in the +# mempool; implies relay), relay (relay even in -blocksonly mode), +# mempool (allow requesting BIP35 mempool contents), download +# (allow getheaders during IBD, no disconnect after maxuploadtarget +# limit), addr (responses to GETADDR avoid hitting the cache and +# contain random records with the most up-to-date info). Specify +# multiple permissions separated by commas (default: +# download,noban,mempool,relay). Can be specified multiple times. +#whitebind=<[permissions@]addr> + +# Add permission flags to the peers connecting from the given IP address +# (e.g. 1.2.3.4) or CIDR-notated network (e.g. 1.2.3.0/24). Uses +# the same permissions as -whitebind. Can be specified multiple +# times. +#whitelist=<[permissions@]IP address or network> + + +### Indexing options + + +# Maintain a full address index, used to query for the balance, txids and +# unspent outputs for addresses (default: 0) +#addressindex=1 + +# Rebuild chain state and block index from the blk*.dat files on disk. +# This will also rebuild active optional indexes. +#reindex=1 + +# Rebuild chain state from the currently indexed blocks. When in pruning +# mode or if blocks on disk might be corrupted, use full -reindex +# instead. Deactivate all optional indexes before running this. +#reindex-chainstate=1 + +# Maintain a full spent index, used to query the spending txid and input +# index for an outpoint (default: 0) +#spentindex=1 + +# Maintain a timestamp index for block hashes, used to query blocks hashes +# by a range of timestamps (default: 0) +#timestampindex=1 + +# Maintain a full transaction index, used by the getrawtransaction rpc +# call (default: 1) +#txindex=1 + + +### Masternode options + + +# Set the username for the "platform user", a restricted user intended to +# be used by Dash Platform, to the specified username. +#deprecated-platform-user= + +# Enable automated quorum data recovery (default: 1) +#llmq-data-recovery= + +# Defines from which LLMQ type the masternode should sync quorum +# verification vectors. Can be used multiple times with different +# LLMQ types. : 0 (sync always from all quorums of the type +# defined by ), 1 (sync from all quorums of the type +# defined by if a member of any of the quorums) +#llmq-qvvec-sync=: + +# Set the masternode BLS private key and enable the client to act as a +# masternode +#masternodeblsprivkey= + + +### Statsd options + + +# Specify the size of each batch of stats messages (default: 1024) +#statsbatchsize= + +# Specify the number of milliseconds between stats messages (default: +# 1000) +#statsduration= + +# Specify statsd host (default: ) +#statshost= + +# Specify the number of seconds between periodic measurements (default: +# 60) +#statsperiod= + +# Specify an optional string prepended to every stats key (default: ) +#statsprefix= + +# Specify an optional string appended to every stats key (default: ) +#statssuffix= + + +### Wallet options + + +# Group outputs by address, selecting many (possibly all) or none, instead +# of selecting on a per-output basis. Privacy is improved as +# addresses are mostly swept with fewer transactions and outputs +# are aggregated in clean change addresses. It may result in higher +# fees due to less optimal coin selection caused by this added +# limitation and possibly a larger-than-necessary number of inputs +# being used. Always enabled for wallets with "avoid_reuse" +# enabled, otherwise default: 0. +#avoidpartialspends=1 + +# The maximum feerate (in DASH/kvB) at which transaction building may use +# more inputs than strictly necessary so that the wallet's UTXO +# pool can be reduced (default: 0.00001). +#consolidatefeerate= + +# Number of automatic wallet backups (default: 10) +#createwalletbackups= + +# Do not load the wallet and disable wallet RPC calls +#disablewallet=1 + +# Execute command when a wallet InstantSend transaction is successfully +# locked. %s in cmd is replaced by TxID and %w is replaced by +# wallet name. %w is not currently implemented on Windows. On +# systems where %w is supported, it should NOT be quoted because +# this would break shell escaping used to invoke the command. +#instantsendnotify= + +# Set key pool size to (default: 1000). Warning: Smaller sizes may +# increase the risk of losing funds when restoring from an old +# backup, if none of the addresses in the original keypool have +# been used. +#keypool= + +# Spend up to this amount in additional (absolute) fees (in DASH) if it +# allows the use of partial spend avoidance (default: 0.00) +#maxapsfee= + +# Rescan the block chain for missing wallet transactions on startup (1 = +# start from wallet creation time, 2 = start from genesis block) +#rescan= + +# External signing tool, see doc/external-signer.md +#signer= + +# Spend unconfirmed change when sending transactions (default: 1) +#spendzeroconfchange=1 + +# Specify wallet path to load at startup. Can be used multiple times to +# load multiple wallets. Path is to a directory containing wallet +# data and log files. If the path is not absolute, it is +# interpreted relative to . This only loads existing +# wallets and does not create new ones. For backwards compatibility +# this also accepts names of existing top-level data files in +# . +#wallet= + +# Specify full path to directory for automatic wallet backups (must exist) +#walletbackupsdir= + +# Make the wallet broadcast transactions (default: 1) +#walletbroadcast=1 + +# Specify directory to hold wallets (default: /wallets if it +# exists, otherwise ) +#walletdir= + +# Execute command when a wallet transaction changes. %s in cmd is replaced +# by TxID, %w is replaced by wallet name, %b is replaced by the +# hash of the block including the transaction (set to 'unconfirmed' +# if the transaction is not included) and %h is replaced by the +# block height (-1 if not included). %w is not currently +# implemented on windows. On systems where %w is supported, it +# should NOT be quoted because this would break shell escaping used +# to invoke the command. +#walletnotify= + + +### Wallet fee options + + +# The fee rate (in DASH/kB) that indicates your tolerance for discarding +# change by adding it to the fee (default: 0.0001). Note: An output +# is discarded if it is dust at this rate, but we will always +# discard up to the dust relay fee and a discard fee above that is +# limited by the fee estimate for the longest target +#discardfee= + +# A fee rate (in DASH/kB) that will be used when fee estimation has +# insufficient data. 0 to entirely disable the fallbackfee feature. +# (default: 0.00001) +#fallbackfee= + +# Fee rates (in DASH/kB) smaller than this are considered zero fee for +# transaction creation (default: 0.00001) +#mintxfee= + +# Fee rate (in DASH/kB) to add to transactions you send (default: 0.00) +#paytxfee= + +# If paytxfee is not set, include enough fee so transactions begin +# confirmation on average within n blocks (default: 6) +#txconfirmtarget= + + +### HD wallet options + + +# User defined seed for HD wallet (should be in hex). Only has effect +# during wallet creation/first start (default: randomly generated) +#hdseed= + +# User defined mnemonic for HD wallet (bip39). Only has effect during +# wallet creation/first start (default: randomly generated) +#mnemonic= + +# User defined mnemonic security for HD wallet in bits (BIP39). Only has +# effect during wallet creation/first start (allowed values: 128, +# 160, 192, 224, 256; default: 128) +#mnemonicbits= + +# User defined mnemonic passphrase for HD wallet (BIP39). Only has effect +# during wallet creation/first start (default: empty string) +#mnemonicpassphrase= + +# Use hierarchical deterministic key generation (HD) after BIP39/BIP44. +# Only has effect during wallet creation/first start (default: 1) +#usehd=1 + + +### CoinJoin options + + +# Target CoinJoin balance (2-21000000, default: 1000) +#coinjoinamount= + +# Start CoinJoin automatically (0-1, default: 0) +#coinjoinautostart=1 + +# Try to create at least N inputs of each denominated amount (10-100000, +# default: 50) +#coinjoindenomsgoal= + +# Create up to N inputs of each denominated amount (10-100000, default: +# 300) +#coinjoindenomshardcap= + +# Enable multiple CoinJoin mixing sessions per block, experimental (0-1, +# default: 0) +#coinjoinmultisession=1 + +# Use N separate masternodes for each denominated input to mix funds +# (2-16, default: 4) +#coinjoinrounds= + +# Use N separate masternodes in parallel to mix funds (1-10, default: 4) +#coinjoinsessions= + +# Enable use of CoinJoin for funds stored in this wallet (0-1, default: 0) +#enablecoinjoin=1 + + +### ZeroMQ notification options + + +# Enable publish hash block in
+#zmqpubhashblock=
+ +# Set publish hash block outbound message high water mark (default: 1000) +#zmqpubhashblockhwm= + +# Enable publish hash block (locked via ChainLocks) in
+#zmqpubhashchainlock=
+ +# Set publish hash chain lock outbound message high water mark (default: +# 1000) +#zmqpubhashchainlockhwm= + +# Enable publish hash of governance objects (like proposals) in
+#zmqpubhashgovernanceobject=
+ +# Set publish hash governance object outbound message high water mark +# (default: 1000) +#zmqpubhashgovernanceobjecthwm= + +# Enable publish hash of governance votes in
+#zmqpubhashgovernancevote=
+ +# Set publish hash governance vote outbound message high water mark +# (default: 1000) +#zmqpubhashgovernancevotehwm= + +# Enable publish transaction hashes of attempted InstantSend double spend +# in
+#zmqpubhashinstantsenddoublespend=
+ +# Set publish hash InstantSend double spend outbound message high water +# mark (default: 1000) +#zmqpubhashinstantsenddoublespendhwm= + +# Enable publish message hash of recovered signatures (recovered by LLMQs) +# in
+#zmqpubhashrecoveredsig=
+ +# Set publish hash recovered signature outbound message high water mark +# (default: 1000) +#zmqpubhashrecoveredsighwm= + +# Enable publish hash transaction in
+#zmqpubhashtx=
+ +# Set publish hash transaction outbound message high water mark (default: +# 1000) +#zmqpubhashtxhwm= + +# Enable publish hash transaction (locked via InstantSend) in
+#zmqpubhashtxlock=
+ +# Set publish hash transaction lock outbound message high water mark +# (default: 1000) +#zmqpubhashtxlockhwm= + +# Enable publish raw block in
+#zmqpubrawblock=
+ +# Set publish raw block outbound message high water mark (default: 1000) +#zmqpubrawblockhwm= + +# Enable publish raw block (locked via ChainLocks) in
+#zmqpubrawchainlock=
+ +# Set publish raw chain lock outbound message high water mark (default: +# 1000) +#zmqpubrawchainlockhwm= + +# Enable publish raw block (locked via ChainLocks) and CLSIG message in +#
+#zmqpubrawchainlocksig=
+ +# Set publish raw chain lock signature outbound message high water mark +# (default: 1000) +#zmqpubrawchainlocksighwm= + +# Enable publish raw governance votes in
+#zmqpubrawgovernanceobject=
+ +# Set publish raw governance object outbound message high water mark +# (default: 1000) +#zmqpubrawgovernanceobjecthwm= + +# Enable publish raw governance objects (like proposals) in
+#zmqpubrawgovernancevote=
+ +# Set publish raw governance vote outbound message high water mark +# (default: 1000) +#zmqpubrawgovernancevotehwm= + +# Enable publish raw transactions of attempted InstantSend double spend in +#
+#zmqpubrawinstantsenddoublespend=
+ +# Set publish raw InstantSend double spend outbound message high water +# mark (default: 1000) +#zmqpubrawinstantsenddoublespendhwm= + +# Enable publish raw recovered signatures (recovered by LLMQs) in +#
+#zmqpubrawrecoveredsig=
+ +# Set publish raw recovered signature outbound message high water mark +# (default: 1000) +#zmqpubrawrecoveredsighwm= + +# Enable publish raw transaction in
+#zmqpubrawtx=
+ +# Set publish raw transaction outbound message high water mark (default: +# 1000) +#zmqpubrawtxhwm= + +# Enable publish raw transaction (locked via InstantSend) in
+#zmqpubrawtxlock=
+ +# Set publish raw transaction lock outbound message high water mark +# (default: 1000) +#zmqpubrawtxlockhwm= + +# Enable publish raw transaction (locked via InstantSend) and ISLOCK in +#
+#zmqpubrawtxlocksig=
+ +# Set publish raw transaction lock signature outbound message high water +# mark (default: 1000) +#zmqpubrawtxlocksighwm= + +# Enable publish hash block and tx sequence in
+#zmqpubsequence=
+ +# Set publish hash sequence message high water mark (default: 1000) +#zmqpubsequencehwm= + + +### Debugging/Testing options + + +# Output debug and trace logging (default: -nodebug, supplying +# is optional). If is not supplied or if = 1, +# output all debug and trace logging. can be: addrman, +# bench, blockstorage, chainlocks, cmpctblock, coindb, coinjoin, +# creditpool, ehf, estimatefee, gobject, http, i2p, instantsend, +# ipc, leveldb, libevent, llmq, llmq-dkg, llmq-sigs, lock, mempool, +# mempoolrej, mnpayments, mnsync, net, netconn, proxy, prune, qt, +# rand, reindex, rpc, selectcoins, spork, tor, txreconciliation, +# validation, walletdb, zmq. This option can be specified multiple +# times to output multiple categories. +#debug= + +# Exclude debug and trace logging for a category. Can be used in +# conjunction with -debug=1 to output debug and trace logging for +# all categories except the specified category. This option can be +# specified multiple times to exclude multiple categories. +#debugexclude= + +# Disable governance validation (0-1, default: 0) +#disablegovernance=1 + +# Print help message with debugging options and exit +#help-debug=1 + +# Include IP addresses in debug output (default: 0) +#logips=1 + +# Always prepend a category and level (default: 0) +#loglevelalways=1 + +# Prepend debug output with name of the originating source location +# (source file, line number and function name) (default: 0) +#logsourcelocations=1 + +# Prepend debug output with timestamp (default: 1) +#logtimestamps=1 + +# Maximum total fees (in DASH) to use in a single wallet transaction; +# setting this too low may abort large transactions (default: 0.10) +#maxtxfee= + +# Overrides minimum spork signers to change spork value. Only useful for +# regtest and devnet. Using this on mainnet or testnet will ban +# you. +#minsporkkeys= + +# Send trace/debug info to console (default: 1 when no -daemon. To disable +# logging to file, set -nodebuglogfile) +#printtoconsole=1 + +# Protocol version to report to other nodes +#pushversion=1 + +# Shrink debug.log file on client startup (default: 1 when no -debug) +#shrinkdebugfile=1 + +# Override spork address. Only useful for regtest and devnet. Using this +# on mainnet or testnet will ban you. +#sporkaddr= + +# Set the private key to be used for signing spork messages. +#sporkkey= + +# Append comment to the user agent string +#uacomment= + + +### Chain selection options + + +# Use the chain (default: main). Allowed values: main, test, +# devnet, regtest +#chain= + +# Use devnet chain with provided name +#devnet= + +# The number of blocks with a higher than normal subsidy to mine at the +# start of a chain. Block after that height will have fixed subsidy +# base. (default: 0, devnet-only) +#highsubsidyblocks= + +# The factor to multiply the normal block subsidy by while in the +# highsubsidyblocks window of a chain (default: 1, devnet-only) +#highsubsidyfactor= + +# Override the default LLMQ type used for ChainLocks. Allows using +# ChainLocks with smaller LLMQs. (default: llmq_devnet, +# devnet-only) +#llmqchainlocks= + +# Override the default LLMQ size for the LLMQ_DEVNET quorum (devnet-only) +#llmqdevnetparams=: + +# Override the default LLMQ type used for InstantSendDIP0024. (default: +# llmq_devnet_dip0024, devnet-only) +#llmqinstantsenddip0024= + +# Override the default LLMQ type used for EHF. (default: llmq_devnet, +# devnet-only) +#llmqmnhf= + +# Override the default LLMQ type used for Platform. (default: +# llmq_devnet_platform, devnet-only) +#llmqplatform= + +# The number of blocks that can be mined with the minimum difficulty at +# the start of a chain (default: 0, devnet-only) +#minimumdifficultyblocks= + +# Override the default PowTargetSpacing value in seconds (default: 2.5 +# minutes, devnet-only) +#powtargetspacing= + +# Use the test chain. Equivalent to -chain=test +#testnet=1 + + +### Node relay options + + +# Equivalent bytes per sigop in transactions for relay and mining +# (default: 20) +#bytespersigop=1 + +# Relay and mine data carrier transactions (default: 1) +#datacarrier=1 + +# Maximum size of data in data carrier transactions we relay and mine +# (default: 83) +#datacarriersize=1 + +# Fees (in DASH/kB) smaller than this are considered zero fee for +# relaying, mining and transaction creation (default: 0.00001) +#minrelaytxfee= + +# Relay non-P2SH multisig (default: 1) +#permitbaremultisig=1 + +# Add 'forcerelay' permission to whitelisted inbound peers with default +# permissions. This will relay transactions even if the +# transactions were already in the mempool. (default: 0) +#whitelistforcerelay=1 + +# Add 'relay' permission to whitelisted inbound peers with default +# permissions. This will accept relayed transactions even when not +# relaying transactions (default: 1) +#whitelistrelay=1 + + +### Block creation options + + +# Set maximum block size in bytes (default: 2000000) +#blockmaxsize= + +# Set lowest fee rate (in DASH/kB) for transactions to be included in +# block creation. (default: 0.00001) +#blockmintxfee= + + +### RPC server options + + +# Accept public REST requests (default: 0) +#rest=1 + +# Allow JSON-RPC connections from specified source. Valid values for +# are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. +# 1.2.3.4/255.255.255.0), a network/CIDR (e.g. 1.2.3.4/24), all +# ipv4 (0.0.0.0/0), or all ipv6 (::/0). This option can be +# specified multiple times +#rpcallowip= + +# Username and HMAC-SHA-256 hashed password for JSON-RPC connections. The +# field comes in the format: :$. A +# canonical python script is included in share/rpcuser. The client +# then connects normally using the +# rpcuser=/rpcpassword= pair of arguments. This +# option can be specified multiple times +#rpcauth= + +# Bind to given address to listen for JSON-RPC connections. Do not expose +# the RPC server to untrusted networks such as the public internet! +# This option is ignored unless -rpcallowip is also passed. Port is +# optional and overrides -rpcport. Use [host]:port notation for +# IPv6. This option can be specified multiple times (default: +# 127.0.0.1 and ::1 i.e., localhost, or if -rpcallowip has been +# specified, 0.0.0.0 and :: i.e., all addresses) +#rpcbind=[:port] + +# Location of the auth cookie. Relative paths will be prefixed by a +# net-specific datadir location. (default: data dir) +#rpccookiefile= + +# List of comma-separated usernames for JSON-RPC external connections +#rpcexternaluser= + +# Password for JSON-RPC connections +#rpcpassword= + +# Listen for JSON-RPC connections on (default: 9998, testnet: +# 19998, devnet: 19798, regtest: 19898) +#rpcport= + +# Set the number of threads to service RPC calls (default: 4) +#rpcthreads= + +# Username for JSON-RPC connections +#rpcuser= + +# Set a whitelist to filter incoming RPC calls for a specific user. The +# field comes in the format: :,,...,. If multiple whitelists are set for a given user, +# they are set-intersected. See -rpcwhitelistdefault documentation +# for information on default whitelist behavior. +#rpcwhitelist= + +# Sets default behavior for rpc whitelisting. Unless rpcwhitelistdefault +# is set to 0, if any -rpcwhitelist is set, the rpc server acts as +# if all rpc users are subject to empty-unless-otherwise-specified +# whitelists. If rpcwhitelistdefault is set to 1 and no +# -rpcwhitelist is set, rpc server acts as if all rpc users are +# subject to empty whitelists. +#rpcwhitelistdefault=1 + +# Accept command line and JSON-RPC commands +#server=1 + + +# [Sections] +# Most options will apply to all networks. To confine an option to a specific +# network, add it under the relevant section below. +# +# Note: If not specified under a network section, the options addnode, connect, +# port, bind, rpcport, rpcbind, and wallet will only apply to mainnet. + +# Options for mainnet +[main] + +# Options for testnet +[test] + +# Options for regtest +[regtest] diff --git a/contrib/devtools/circular-dependencies.py b/contrib/devtools/circular-dependencies.py index e939ec6d45ba..a6cdc343d5cb 100755 --- a/contrib/devtools/circular-dependencies.py +++ b/contrib/devtools/circular-dependencies.py @@ -5,7 +5,7 @@ import sys import re -from multiprocess import Pool # type: ignore[import] +import multiprocessing from typing import Dict, List, Set MAPPING = { @@ -33,10 +33,32 @@ def module_name(path): return path[:-4] return None -if __name__=="__main__": - files = dict() - deps: Dict[str, Set[str]] = dict() +files = dict() +deps: Dict[str, Set[str]] = dict() + +# Defined at module level (reading the global `deps`) so it pickles by reference +# for multiprocessing.Pool; forked workers inherit the populated `deps`. +def handle_module2(module): + # Build the transitive closure of dependencies of module + closure: Dict[str, List[str]] = dict() + for dep in deps[module]: + closure[dep] = [] + while True: + old_size = len(closure) + old_closure_keys = sorted(closure.keys()) + for src in old_closure_keys: + for dep in deps[src]: + if dep not in closure: + closure[dep] = closure[src] + [src] + if len(closure) == old_size: + break + # If module is in its own transitive closure, it's a circular dependency; check if it is the shortest + if module in closure: + return [module] + closure[module] + + return None +if __name__=="__main__": RE = re.compile("^#include <(.*)>") def handle_module(arg): @@ -47,27 +69,6 @@ def handle_module(arg): files[arg] = module deps[module] = set() - def handle_module2(module): - # Build the transitive closure of dependencies of module - closure: Dict[str, List[str]] = dict() - for dep in deps[module]: - closure[dep] = [] - while True: - old_size = len(closure) - old_closure_keys = sorted(closure.keys()) - for src in old_closure_keys: - for dep in deps[src]: - if dep not in closure: - closure[dep] = closure[src] + [src] - if len(closure) == old_size: - break - # If module is in its own transitive closure, it's a circular dependency; check if it is the shortest - if module in closure: - return [module] + closure[module] - - return None - - # Iterate over files, and create list of modules for arg in sys.argv[1:]: handle_module(arg) @@ -101,8 +102,14 @@ def shortest_c_dep(): if sorted_keys is None: sorted_keys = sorted(deps.keys()) - with Pool(8) as pool: - cycles = pool.map(handle_module2, sorted_keys) + # Use fork so workers inherit the populated `deps` global without + # having to pickle it for every task. fork is unavailable on + # Windows, so fall back to a serial map there. + if "fork" in multiprocessing.get_all_start_methods(): + with multiprocessing.get_context("fork").Pool(8) as pool: + cycles = pool.map(handle_module2, sorted_keys) + else: + cycles = list(map(handle_module2, sorted_keys)) for cycle in cycles: if cycle is not None and (shortest_cycles is None or len(cycle) < len(shortest_cycles)): diff --git a/contrib/flatpak/org.dash.dash-core.metainfo.xml b/contrib/flatpak/org.dash.dash-core.metainfo.xml index 30227f6b4771..e9464a179d74 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/depends/packages/freetype.mk b/depends/packages/freetype.mk index fef0beaa7b49..a97f82e7feae 100644 --- a/depends/packages/freetype.mk +++ b/depends/packages/freetype.mk @@ -4,6 +4,7 @@ $(package)_download_path=https://download.savannah.gnu.org/releases/$(package) $(package)_file_name=$(package)-$($(package)_version).tar.xz $(package)_sha256_hash=8bee39bd3968c4804b70614a0a3ad597299ad0e824bc8aad5ce8aaf48067bde7 $(package)_build_subdir=build +$(package)_patches += cmake_minimum.patch define $(package)_set_vars $(package)_config_opts := -DCMAKE_BUILD_TYPE=None -DBUILD_SHARED_LIBS=TRUE @@ -12,6 +13,10 @@ define $(package)_set_vars $(package)_config_opts += -DCMAKE_DISABLE_FIND_PACKAGE_BrotliDec=TRUE endef +define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/cmake_minimum.patch +endef + define $(package)_config_cmds $($(package)_cmake) -S .. -B . endef diff --git a/depends/patches/freetype/cmake_minimum.patch b/depends/patches/freetype/cmake_minimum.patch new file mode 100644 index 000000000000..0a976f8ab8d9 --- /dev/null +++ b/depends/patches/freetype/cmake_minimum.patch @@ -0,0 +1,13 @@ +build: set minimum required CMake to 3.12 + +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -97,7 +97,7 @@ + # FreeType explicitly marks the API to be exported and relies on the compiler + # to hide all other symbols. CMake supports a C_VISBILITY_PRESET property + # starting with 2.8.12. +-cmake_minimum_required(VERSION 2.8.12) ++cmake_minimum_required(VERSION 3.12) + + if (NOT CMAKE_VERSION VERSION_LESS 3.3) + # Allow symbol visibility settings also on static libraries. CMake < 3.3 diff --git a/doc/man/dash-cli.1 b/doc/man/dash-cli.1 index 2d5739f408f7..8bf92ce8161b 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" "June 2026" "dash-cli v23.1.7" "User Commands" +.TH DASH-CLI "1" "July 2026" "dash-cli v23.1.8" "User Commands" .SH NAME -dash-cli \- manual page for dash-cli v23.1.7 +dash-cli \- manual page for dash-cli v23.1.8 .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.7 .B dash-cli [\fI\,options\/\fR] \fI\,help Get help for a command\/\fR .SH DESCRIPTION -Dash Core RPC client version v23.1.7 +Dash Core RPC client version v23.1.8 .SH OPTIONS .HP \-? diff --git a/doc/man/dash-qt.1 b/doc/man/dash-qt.1 index d906b5960f6c..1189ea73bb59 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" "June 2026" "dash-qt v23.1.7" "User Commands" +.TH DASH-QT "1" "July 2026" "dash-qt v23.1.8" "User Commands" .SH NAME -dash-qt \- manual page for dash-qt v23.1.7 +dash-qt \- manual page for dash-qt v23.1.8 .SH SYNOPSIS .B dash-qt [\fI\,command-line options\/\fR] [\fI\,URI\/\fR] .SH DESCRIPTION -Dash Core version v23.1.7 +Dash Core version v23.1.8 .PP Optional URI is a Dash address in BIP21 URI format. .SH OPTIONS @@ -128,13 +128,13 @@ Do not keep transactions in the mempool longer than hours (default: .HP \fB\-par=\fR .IP -Set the number of script verification threads (\fB\-14\fR to 15, 0 = auto, <0 = -leave that many cores free, default: 0) +Set the number of script verification threads (0 = auto, <0 = leave that many +cores free, max: 15, default: 0) .HP \fB\-parbls=\fR .IP -Set the number of BLS verification threads (\fB\-14\fR to 33, 0 = auto, <0 = -leave that many cores free, default: 0) +Set the number of BLS verification threads (0 = auto, <0 = leave that many +cores free, max: 33, default: 0) .HP \fB\-persistmempool\fR .IP @@ -881,7 +881,7 @@ is optional). If is not supplied or if = 1, output all debug and trace logging. can be: addrman, bench, blockstorage, chainlocks, cmpctblock, coindb, coinjoin, creditpool, ehf, estimatefee, gobject, http, i2p, instantsend, -ipc, leveldb, libevent, llmq, llmq\-dkg, llmq\-sigs, mempool, +ipc, leveldb, libevent, llmq, llmq\-dkg, llmq\-sigs, lock, mempool, mempoolrej, mnpayments, mnsync, net, netconn, proxy, prune, qt, rand, reindex, rpc, selectcoins, spork, tor, txreconciliation, validation, walletdb, zmq. This option can be specified multiple diff --git a/doc/man/dash-tx.1 b/doc/man/dash-tx.1 index fa7fc530064b..7b587eeb9f19 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" "June 2026" "dash-tx v23.1.7" "User Commands" +.TH DASH-TX "1" "July 2026" "dash-tx v23.1.8" "User Commands" .SH NAME -dash-tx \- manual page for dash-tx v23.1.7 +dash-tx \- manual page for dash-tx v23.1.8 .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.7 .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.7 +Dash Core dash\-tx utility version v23.1.8 .SH OPTIONS .HP \-? diff --git a/doc/man/dash-util.1 b/doc/man/dash-util.1 index f1a1e4ccf654..3a47b0b8037a 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" "June 2026" "dash-util v23.1.7" "User Commands" +.TH DASH-UTIL "1" "July 2026" "dash-util v23.1.8" "User Commands" .SH NAME -dash-util \- manual page for dash-util v23.1.7 +dash-util \- manual page for dash-util v23.1.8 .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.7 +Dash Core dash\-util utility version v23.1.8 .SH OPTIONS .HP \-? diff --git a/doc/man/dash-wallet.1 b/doc/man/dash-wallet.1 index b942f301f180..ffeaccdb710f 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" "June 2026" "dash-wallet v23.1.7" "User Commands" +.TH DASH-WALLET "1" "July 2026" "dash-wallet v23.1.8" "User Commands" .SH NAME -dash-wallet \- manual page for dash-wallet v23.1.7 +dash-wallet \- manual page for dash-wallet v23.1.8 .SH DESCRIPTION -Dash Core dash\-wallet version v23.1.7 +Dash Core dash\-wallet version v23.1.8 .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 011b448337c3..ebb4a835ec0b 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" "June 2026" "dashd v23.1.7" "User Commands" +.TH DASHD "1" "July 2026" "dashd v23.1.8" "User Commands" .SH NAME -dashd \- manual page for dashd v23.1.7 +dashd \- manual page for dashd v23.1.8 .SH SYNOPSIS .B dashd [\fI\,options\/\fR] \fI\,Start Dash Core\/\fR .SH DESCRIPTION -Dash Core version v23.1.7 +Dash Core version v23.1.8 .SH OPTIONS .HP \-? @@ -126,13 +126,13 @@ Do not keep transactions in the mempool longer than hours (default: .HP \fB\-par=\fR .IP -Set the number of script verification threads (\fB\-14\fR to 15, 0 = auto, <0 = -leave that many cores free, default: 0) +Set the number of script verification threads (0 = auto, <0 = leave that many +cores free, max: 15, default: 0) .HP \fB\-parbls=\fR .IP -Set the number of BLS verification threads (\fB\-14\fR to 33, 0 = auto, <0 = -leave that many cores free, default: 0) +Set the number of BLS verification threads (0 = auto, <0 = leave that many +cores free, max: 33, default: 0) .HP \fB\-persistmempool\fR .IP @@ -879,7 +879,7 @@ is optional). If is not supplied or if = 1, output all debug and trace logging. can be: addrman, bench, blockstorage, chainlocks, cmpctblock, coindb, coinjoin, creditpool, ehf, estimatefee, gobject, http, i2p, instantsend, -ipc, leveldb, libevent, llmq, llmq\-dkg, llmq\-sigs, mempool, +ipc, leveldb, libevent, llmq, llmq\-dkg, llmq\-sigs, lock, mempool, mempoolrej, mnpayments, mnsync, net, netconn, proxy, prune, qt, rand, reindex, rpc, selectcoins, spork, tor, txreconciliation, validation, walletdb, zmq. This option can be specified multiple diff --git a/doc/release-notes.md b/doc/release-notes.md index 5e4e5e7628b3..3d0630088490 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -1,7 +1,8 @@ -# Dash Core version v23.1.7 +# Dash Core version v23.1.8 -This is a new patch version release, bringing security hardening and build fixes -for newer compiler toolchains. +This is a patch release with additional security hardening, networking and +CoinJoin reliability fixes, and compatibility updates for newer build and CI +toolchains. This release is **recommended** for all nodes, and especially for masternodes. Please report bugs using the issue tracker at GitHub: @@ -26,34 +27,72 @@ require a reindex. # Release Notes -## Security - -This release hardens several 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. - -- Networking: a peer whose receive buffer filled up could keep the socket-handler - thread spinning at 100% CPU for the duration of the backpressure. The thread now - falls back to its normal poll wait while such peers are paused. -- LLMQ / DKG: pushed DKG messages are now accepted only from verified masternodes, - are bounded in size, and are structurally validated before being retained; - malformed signatures can no longer trigger an assertion failure during batch - signature verification. -- BLS: verifying a DKG contribution share whose verification vector was never - received no longer dereferences a null pointer. -- InstantSend: locks with an oversized input set are now rejected before any - expensive processing, and the queues holding not-yet-verified and - awaiting-transaction locks are bounded to prevent unbounded memory growth. -- Governance: vote-sync requests carrying a bloom filter outside the permitted size - are rejected, preventing a CPU-amplification stall of P2P message processing. - -## Build - -- Fixed GCC 16 build failures in warning-enabled builds by tightening header - includes and initializing LevelDB compaction output size. - -# v23.1.7 Change log +## Security and P2P hardening + +This release adds bounds and request authorization to several peer-to-peer +message paths. These changes do not alter consensus or put funds at risk; they +reduce the ability of remote peers to consume excessive memory or CPU, retain +unbounded work, or send unsolicited governance data. + +- Added shared bounded-vector deserialization and applied it to SPORK signatures, + bloom filters, filteradd payloads, quorum data, LLMQ signing messages, and + CoinJoin entry and final-signature messages. Oversized wire counts are rejected + before allocating or decoding their full contents. +- Bounded pending recovered-signature and signature-share queues, the number and + size of unverified signature-share batches, and aggregate batched-signature + intake. +- Bounded the ChainLock seen cache. +- Governance vote-sync requests are throttled per object. Governance object and + vote responses are accepted only when the sending peer announced the item or + was asked for it through the existing per-peer request tracker. +- Compact-block relay was hardened against mutated blocks, malformed or empty + headers, reconstruction failures, and parallel-download state corruption. +- Block and block-transaction disk reads no longer hold `cs_main`, reducing lock + contention while serving peers. + +## CoinJoin and wallet + +- Fixed CoinJoin client lifetime handling by executing client callbacks while the + wallet-manager map remains locked, avoiding dangling client pointers during + wallet removal. +- CoinJoin initialization now follows wallet addition, the configured CoinJoin + preference remains consistent across UI and command-line paths, and locked + wallets are no longer automatically started for mixing. +- Moved mixing state onto the wallet to avoid a wallet-manager lock-order + deadlock, with regression coverage for new-keypool callbacks and the + wallet-manager lock-order cycle. +- CoinJoin wire and semantic limits are separated so protocol payloads are + rejected at the appropriate boundary without disrupting other session + participants. + +## GUI and RPC + +- The masternode PoSe score remains visible when banned masternodes are hidden. +- The CoinJoin toggle now reflects externally started mixing and remains on the + Start action when a local start attempt fails. +- Internal platform-address migration no longer produces spurious `protx + listdiff` changes. When deprecated platform port fields are present alongside + migrated address data, `platformP2PPort` and `platformHTTPPort` report the + corresponding non-zero values instead of stale zeroes. + +## Build, CI, and developer tooling + +- Fixed the depends Freetype build with CMake 4 compatibility requirements. +- Updated GitHub Actions used by repository workflows to Node 24-compatible + releases while preserving the release branch's existing Docker action pins. +- Updated the circular-dependency checker for Python 3.15, including platforms + where process forking is unavailable. +- Retained the GCC 15/16 compatibility and LevelDB fixes already shipped in + v23.1.7. + +## Tests + +New and expanded unit, fuzz, and functional coverage exercises bounded vector +serialization, LLMQ queues and message intake, governance request authorization, +ChainLock cache eviction, CoinJoin boundaries and wallet callbacks, SPORK and +bloom-filter limits, compact-block reconstruction, and mutated blocks. + +# v23.1.8 Change log See detailed [set of changes][set-of-changes]. @@ -61,8 +100,12 @@ See detailed [set of changes][set-of-changes]. Thanks to everyone who directly contributed to this release: -- knst -- PastaPastaPasta +- Claude Code +- Konstantin Akimov +- MarcoFalke +- pasta +- PastaClaw +- UdjinM6 As well as everyone that submitted issues, reviewed pull requests and helped debug the release candidates. @@ -71,6 +114,7 @@ debug the release candidates. 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 @@ -89,4 +133,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.5...dashpay:v23.1.7 +[set-of-changes]: https://github.com/dashpay/dash/compare/v23.1.7...dashpay:v23.1.8 diff --git a/doc/release-notes/dash/release-notes-23.1.7.md b/doc/release-notes/dash/release-notes-23.1.7.md new file mode 100644 index 000000000000..5e4e5e7628b3 --- /dev/null +++ b/doc/release-notes/dash/release-notes-23.1.7.md @@ -0,0 +1,92 @@ +# Dash Core version v23.1.7 + +This is a new patch version release, bringing security hardening and build fixes +for newer compiler toolchains. +This release is **recommended** for all nodes, and especially 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 + +## Security + +This release hardens several 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. + +- Networking: a peer whose receive buffer filled up could keep the socket-handler + thread spinning at 100% CPU for the duration of the backpressure. The thread now + falls back to its normal poll wait while such peers are paused. +- LLMQ / DKG: pushed DKG messages are now accepted only from verified masternodes, + are bounded in size, and are structurally validated before being retained; + malformed signatures can no longer trigger an assertion failure during batch + signature verification. +- BLS: verifying a DKG contribution share whose verification vector was never + received no longer dereferences a null pointer. +- InstantSend: locks with an oversized input set are now rejected before any + expensive processing, and the queues holding not-yet-verified and + awaiting-transaction locks are bounded to prevent unbounded memory growth. +- Governance: vote-sync requests carrying a bloom filter outside the permitted size + are rejected, preventing a CPU-amplification stall of P2P message processing. + +## Build + +- Fixed GCC 16 build failures in warning-enabled builds by tightening header + includes and initializing LevelDB compaction output size. + +# v23.1.7 Change log + +See detailed [set of changes][set-of-changes]. + +# Credits + +Thanks to everyone who directly contributed to this release: + +- knst +- PastaPastaPasta + +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.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.5...dashpay:v23.1.7 diff --git a/src/Makefile.am b/src/Makefile.am index 55ef13001b68..19f972054635 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -505,7 +505,6 @@ libbitcoin_node_a_SOURCES = \ chainlock/signing.cpp \ coinjoin/coinjoin.cpp \ coinjoin/server.cpp \ - coinjoin/walletman.cpp \ consensus/tx_verify.cpp \ dbwrapper.cpp \ deploymentstatus.cpp \ @@ -662,6 +661,7 @@ libbitcoin_wallet_a_SOURCES = \ coinjoin/client.cpp \ coinjoin/interfaces.cpp \ coinjoin/util.cpp \ + coinjoin/walletman.cpp \ wallet/bip39.cpp \ wallet/coinjoin.cpp \ wallet/coincontrol.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index dd6dda7178c3..ffa3c1616fe4 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -119,6 +119,7 @@ BITCOIN_TESTS =\ test/flatfile_tests.cpp \ test/fs_tests.cpp \ test/getarg_tests.cpp \ + test/governance_inv_tests.cpp \ test/governance_validators_tests.cpp \ test/coinjoin_inouts_tests.cpp \ test/coinjoin_dstxmanager_tests.cpp \ @@ -336,6 +337,7 @@ test_fuzz_fuzz_SOURCES = \ test/fuzz/parse_numbers.cpp \ test/fuzz/parse_script.cpp \ test/fuzz/parse_univalue.cpp \ + test/fuzz/partially_downloaded_block.cpp \ test/fuzz/policy_estimator.cpp \ test/fuzz/policy_estimator_io.cpp \ test/fuzz/poolresource.cpp \ diff --git a/src/active/quorums.cpp b/src/active/quorums.cpp index c6c82f219bef..28a5675aeebc 100644 --- a/src/active/quorums.cpp +++ b/src/active/quorums.cpp @@ -24,6 +24,8 @@ #include +#include + namespace llmq { QuorumParticipant::QuorumParticipant(CBLSWorker& bls_worker, CConnman& connman, CDeterministicMNManager& dmnman, QuorumObserverParent& qman, CQuorumSnapshotManager& qsnapman, @@ -152,7 +154,12 @@ MessageProcessingResult QuorumParticipant::ProcessContribQDATA(CNode& pfrom, CDa } std::vector> vecEncrypted; - vStream >> vecEncrypted; + const size_t expected_contributions{static_cast(std::count(quorum.qc->validMembers.begin(), + quorum.qc->validMembers.end(), true))}; + if (!UnserializeVectorWithMaxSize(vStream, vecEncrypted, expected_contributions) || + vecEncrypted.size() != expected_contributions) { + return MisbehavingError{100, "invalid encrypted contribution vector size"}; + } std::vector vecSecretKeys; vecSecretKeys.resize(vecEncrypted.size()); diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp index b66c98e8e80c..f12b45b49354 100644 --- a/src/blockencodings.cpp +++ b/src/blockencodings.cpp @@ -54,7 +54,8 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c if (cmpctblock.shorttxids.size() + cmpctblock.prefilledtxn.size() > MaxBlockSize() / MIN_TRANSACTION_SIZE) return READ_STATUS_INVALID; - assert(header.IsNull() && txn_available.empty()); + if (!header.IsNull() || !txn_available.empty()) return READ_STATUS_INVALID; + header = cmpctblock.header; txn_available.resize(cmpctblock.BlockTxCount()); @@ -169,14 +170,18 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c return READ_STATUS_OK; } -bool PartiallyDownloadedBlock::IsTxAvailable(size_t index) const { - assert(!header.IsNull()); +bool PartiallyDownloadedBlock::IsTxAvailable(size_t index) const +{ + if (header.IsNull()) return false; + assert(index < txn_available.size()); return txn_available[index] != nullptr; } -ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector& vtx_missing) { - assert(!header.IsNull()); +ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector& vtx_missing) +{ + if (header.IsNull()) return READ_STATUS_INVALID; + uint256 hash = header.GetHash(); block = header; block.vtx.resize(txn_available.size()); @@ -198,15 +203,10 @@ ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector< if (vtx_missing.size() != tx_missing_offset) return READ_STATUS_INVALID; - BlockValidationState state; - if (!CheckBlock(block, state, Params().GetConsensus())) { - // TODO: We really want to just check merkle tree manually here, - // but that is expensive, and CheckBlock caches a block's - // "checked-status" (in the CBlock?). CBlock should be able to - // check its own merkle root and cache that check. - if (state.GetResult() == BlockValidationResult::BLOCK_MUTATED) - return READ_STATUS_FAILED; // Possible Short ID collision - return READ_STATUS_CHECKBLOCK_FAILED; + // Check for possible mutations early now that we have a seemingly good block + IsBlockMutatedFn check_mutated{m_check_block_mutated_mock ? m_check_block_mutated_mock : IsBlockMutated}; + if (check_mutated(/*block=*/block)) { + return READ_STATUS_FAILED; // Possible Short ID collision } LogPrint(BCLog::CMPCTBLOCK, "Successfully reconstructed block %s with %lu txn prefilled, %lu txn from mempool (incl at least %lu from extra pool) and %lu txn requested\n", hash.ToString(), prefilled_count, mempool_count, extra_count, vtx_missing.size()); diff --git a/src/blockencodings.h b/src/blockencodings.h index a19db7db2dd3..61210ef9b78f 100644 --- a/src/blockencodings.h +++ b/src/blockencodings.h @@ -7,8 +7,13 @@ #include +#include class CTxMemPool; +class BlockValidationState; +namespace Consensus { +struct Params; +}; // Transaction compression schemes for compact block relay can be introduced by writing // an actual formatter here. @@ -79,8 +84,6 @@ typedef enum ReadStatus_t READ_STATUS_OK, READ_STATUS_INVALID, // Invalid object, peer is sending bogus crap READ_STATUS_FAILED, // Failed to process object - READ_STATUS_CHECKBLOCK_FAILED, // Used only by FillBlock to indicate a - // failure in CheckBlock. } ReadStatus; class CBlockHeaderAndShortTxIDs { @@ -129,6 +132,11 @@ class PartiallyDownloadedBlock { const CTxMemPool* pool; public: CBlockHeader header; + + // Can be overriden for testing + using IsBlockMutatedFn = std::function; + IsBlockMutatedFn m_check_block_mutated_mock{nullptr}; + explicit PartiallyDownloadedBlock(CTxMemPool* poolIn) : pool(poolIn) {} // extra_txn is a list of extra transactions to look at, in form diff --git a/src/chainlock/handler.cpp b/src/chainlock/handler.cpp index d9addc2e70b2..022fdee7e637 100644 --- a/src/chainlock/handler.cpp +++ b/src/chainlock/handler.cpp @@ -44,7 +44,8 @@ ChainlockHandler::ChainlockHandler(chainlock::Chainlocks& chainlocks, Chainstate m_mn_sync{mn_sync}, scheduler{std::make_unique()}, scheduler_thread{ - std::make_unique(std::thread(util::TraceThread, "cl-schdlr", [&] { scheduler->serviceQueue(); }))} + std::make_unique(std::thread(util::TraceThread, "cl-schdlr", [&] { scheduler->serviceQueue(); }))}, + seenChainLocks{MAX_SEEN_CHAINLOCKS} { } @@ -68,9 +69,28 @@ void ChainlockHandler::Start() void ChainlockHandler::Stop() { scheduler->stop(); } bool ChainlockHandler::AlreadyHave(const CInv& inv) const +{ + { + LOCK(cs); + if (seenChainLocks.count(inv.hash) != 0) { + return true; + } + } + + chainlock::ChainLockSig clsig; + return m_chainlocks.GetChainLockByHash(inv.hash, clsig); +} + +size_t ChainlockHandler::SeenChainLockCacheSizeForTesting() const +{ + LOCK(cs); + return seenChainLocks.size(); +} + +size_t ChainlockHandler::SeenChainLockCacheMaxSizeForTesting() const { LOCK(cs); - return seenChainLocks.count(inv.hash) != 0; + return seenChainLocks.max_size(); } void ChainlockHandler::UpdateTxFirstSeenMap(const Uint256HashSet& tx, const int64_t& time) @@ -89,13 +109,16 @@ MessageProcessingResult ChainlockHandler::ProcessNewChainLock(const NodeId from, { LOCK(cs); - if (!seenChainLocks.emplace(hash, GetTime()).second) { + if (seenChainLocks.count(hash) != 0) { return {}; } + seenChainLocks.insert({hash, GetTime()}); - // height is expect to check twice: preliminary (for optimization) and inside UpdateBestsChainlock (as mutex is not kept during validation) + // Height is checked twice: preliminary (for optimization) and inside + // UpdateBestChainlock, as this mutex is not kept during validation. if (clsig.getHeight() <= m_chainlocks.GetBestChainLockHeight()) { - // no need to process older/same CLSIGs + // Remember the hash so AlreadyHave() suppresses repeated requests + // for stale CLSIG announcements. return {}; } } @@ -293,7 +316,9 @@ void ChainlockHandler::Cleanup() LOCK(cs); for (auto it = seenChainLocks.begin(); it != seenChainLocks.end();) { if (GetTime() - it->second >= CLEANUP_SEEN_TIMEOUT) { - it = seenChainLocks.erase(it); + const auto hash = it->first; + ++it; + seenChainLocks.erase(hash); } else { ++it; } diff --git a/src/chainlock/handler.h b/src/chainlock/handler.h index e024ca539876..979133263e07 100644 --- a/src/chainlock/handler.h +++ b/src/chainlock/handler.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_CHAINLOCK_HANDLER_H #define BITCOIN_CHAINLOCK_HANDLER_H +#include #include #include #include @@ -59,10 +60,12 @@ class ChainlockHandler final : public CValidationInterface std::atomic tryLockChainTipScheduled{false}; std::atomic isEnabled{false}; + static constexpr size_t MAX_SEEN_CHAINLOCKS{1024}; + const CBlockIndex* lastNotifyChainLockBlockIndex GUARDED_BY(cs){nullptr}; Uint256HashMap txFirstSeenTime GUARDED_BY(cs); - std::map seenChainLocks GUARDED_BY(cs); + unordered_limitedmap seenChainLocks GUARDED_BY(cs); CleanupThrottler cleanupThrottler; @@ -79,6 +82,8 @@ class ChainlockHandler final : public CValidationInterface bool AlreadyHave(const CInv& inv) const EXCLUSIVE_LOCKS_REQUIRED(!cs); void UpdateTxFirstSeenMap(const Uint256HashSet& tx, const int64_t& time) EXCLUSIVE_LOCKS_REQUIRED(!cs); + size_t SeenChainLockCacheSizeForTesting() const EXCLUSIVE_LOCKS_REQUIRED(!cs); + size_t SeenChainLockCacheMaxSizeForTesting() const EXCLUSIVE_LOCKS_REQUIRED(!cs); [[nodiscard]] MessageProcessingResult ProcessNewChainLock(NodeId from, const chainlock::ChainLockSig& clsig, const llmq::CQuorumManager& qman, diff --git a/src/coinjoin/client.cpp b/src/coinjoin/client.cpp index be6e7b458115..531d5e25565e 100644 --- a/src/coinjoin/client.cpp +++ b/src/coinjoin/client.cpp @@ -172,8 +172,8 @@ void CCoinJoinClientManager::ProcessMessage(CNode& peer, CChainState& active_cha if (!m_mn_sync.IsBlockchainSynced()) return; if (!CheckDiskSpace(gArgs.GetDataDirNet())) { - ResetPool(); - StopMixing(); + resetPool(); + stopMixing(); WalletCJLogPrint(m_wallet, "CCoinJoinClientManager::ProcessMessage -- Not enough disk space, disabling CoinJoin.\n"); return; } @@ -250,18 +250,17 @@ void CCoinJoinClientSession::ProcessMessage(CNode& peer, CChainState& active_cha } } -bool CCoinJoinClientManager::StartMixing() { - bool expected{false}; - return fMixing.compare_exchange_strong(expected, true); +bool CCoinJoinClientManager::startMixing() { + return m_wallet->StartMixing(); } -void CCoinJoinClientManager::StopMixing() { - fMixing = false; +void CCoinJoinClientManager::stopMixing() { + m_wallet->StopMixing(); } -bool CCoinJoinClientManager::IsMixing() const +bool CCoinJoinClientManager::isMixing() const { - return fMixing; + return m_wallet->IsMixing(); } void CCoinJoinClientSession::ResetPool() @@ -272,7 +271,7 @@ void CCoinJoinClientSession::ResetPool() WITH_LOCK(cs_coinjoin, SetNull()); } -void CCoinJoinClientManager::ResetPool() +void CCoinJoinClientManager::resetPool() { nCachedLastSuccessBlock = 0; AssertLockNotHeld(cs_deqsessions); @@ -354,7 +353,7 @@ bilingual_str CCoinJoinClientSession::GetStatus(bool fWaitForBlock) const } } -std::vector CCoinJoinClientManager::GetStatuses() const +std::vector CCoinJoinClientManager::getSessionStatuses() const { AssertLockNotHeld(cs_deqsessions); @@ -368,7 +367,7 @@ std::vector CCoinJoinClientManager::GetStatuses() const return ret; } -std::string CCoinJoinClientManager::GetSessionDenoms() +std::string CCoinJoinClientManager::getSessionDenoms() const { std::string strSessionDenoms; @@ -441,7 +440,7 @@ void CCoinJoinClientManager::CheckTimeout() { AssertLockNotHeld(cs_deqsessions); - if (!CCoinJoinClientOptions::IsEnabled() || !IsMixing()) return; + if (!CCoinJoinClientOptions::IsEnabled() || !isMixing()) return; LOCK(cs_deqsessions); for (auto& session : deqSessions) { @@ -719,7 +718,7 @@ bool CCoinJoinClientManager::WaitForAnotherBlock() const bool CCoinJoinClientManager::CheckAutomaticBackup() { - if (!CCoinJoinClientOptions::IsEnabled() || !IsMixing()) return false; + if (!CCoinJoinClientOptions::IsEnabled() || !isMixing()) return false; // We don't need auto-backups for descriptor wallets if (!m_wallet->IsLegacy()) return true; @@ -728,7 +727,7 @@ bool CCoinJoinClientManager::CheckAutomaticBackup() case 0: strAutoDenomResult = _("Automatic backups disabled") + Untranslated(", ") + _("no mixing available."); WalletCJLogPrint(m_wallet, "CCoinJoinClientManager::CheckAutomaticBackup -- %s\n", strAutoDenomResult.original); - StopMixing(); + stopMixing(); m_wallet->nKeysLeftSinceAutoBackup = 0; // no backup, no "keys since last backup" return false; case -1: @@ -754,7 +753,7 @@ bool CCoinJoinClientManager::CheckAutomaticBackup() m_wallet->nKeysLeftSinceAutoBackup); WalletCJLogPrint(m_wallet, "CCoinJoinClientManager::CheckAutomaticBackup -- %s\n", strAutoDenomResult.original); // It's getting really dangerous, stop mixing - StopMixing(); + stopMixing(); return false; } else if (m_wallet->nKeysLeftSinceAutoBackup < COINJOIN_KEYS_THRESHOLD_WARNING) { // Low number of keys left, but it's still more or less safe to continue @@ -976,7 +975,7 @@ bool CCoinJoinClientSession::DoAutomaticDenominating(ChainstateManager& chainman bool CCoinJoinClientManager::DoAutomaticDenominating(ChainstateManager& chainman, CConnman& connman, const CTxMemPool& mempool, bool fDryRun) { - if (!CCoinJoinClientOptions::IsEnabled() || !IsMixing()) return false; + if (!CCoinJoinClientOptions::IsEnabled() || !isMixing()) return false; if (!m_mn_sync.IsBlockchainSynced()) { strAutoDenomResult = _("Can't mix while sync in progress."); @@ -1873,10 +1872,10 @@ void CCoinJoinClientSession::GetJsonInfo(UniValue& obj) const obj.pushKV("entries_count", GetEntriesCount()); } -void CCoinJoinClientManager::GetJsonInfo(UniValue& obj) const +UniValue CCoinJoinClientManager::getJsonInfo() const { - assert(obj.isObject()); - obj.pushKV("running", IsMixing()); + UniValue obj(UniValue::VOBJ); + obj.pushKV("running", isMixing()); UniValue arrSessions(UniValue::VARR); AssertLockNotHeld(cs_deqsessions); @@ -1889,6 +1888,7 @@ void CCoinJoinClientManager::GetJsonInfo(UniValue& obj) const } } obj.pushKV("sessions", arrSessions); + return obj; } CoinJoinWalletManager::CoinJoinWalletManager(ChainstateManager& chainman, CDeterministicMNManager& dmnman, @@ -1913,12 +1913,13 @@ CoinJoinWalletManager::~CoinJoinWalletManager() } } -void CoinJoinWalletManager::Add(const std::shared_ptr& wallet) +bool CoinJoinWalletManager::Add(const std::shared_ptr& wallet) { LOCK(cs_wallet_manager_map); - m_wallet_manager_map.try_emplace(wallet->GetName(), - std::make_unique(wallet, m_dmnman, m_mn_metaman, m_mn_sync, - m_isman, m_queueman)); + return m_wallet_manager_map.try_emplace(wallet->GetName(), + std::make_unique(wallet, m_dmnman, m_mn_metaman, + m_mn_sync, m_isman, m_queueman)) + .second; } void CoinJoinWalletManager::DoMaintenance(CConnman& connman) @@ -1936,9 +1937,19 @@ void CoinJoinWalletManager::Remove(const std::string& name) { void CoinJoinWalletManager::Flush(const std::string& name) { - auto clientman = Assert(Get(name)); - clientman->ResetPool(); - clientman->StopMixing(); + DoForClient(name, [](CCoinJoinClientManager& clientman) { + clientman.resetPool(); + clientman.stopMixing(); + }); +} + +bool CoinJoinWalletManager::DoForClient(const std::string& name, const std::function& func) +{ + LOCK(cs_wallet_manager_map); + auto it = m_wallet_manager_map.find(name); + if (it == m_wallet_manager_map.end()) return false; + func(*it->second); + return true; } CCoinJoinClientManager* CoinJoinWalletManager::Get(const std::string& name) const diff --git a/src/coinjoin/client.h b/src/coinjoin/client.h index 2b1933841c71..6ab9a3b86183 100644 --- a/src/coinjoin/client.h +++ b/src/coinjoin/client.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -15,7 +16,6 @@ #include #include -#include #include #include #include @@ -84,13 +84,15 @@ class CoinJoinWalletManager { const std::unique_ptr& queueman); ~CoinJoinWalletManager(); - void Add(const std::shared_ptr& wallet) EXCLUSIVE_LOCKS_REQUIRED(!cs_wallet_manager_map); + bool Add(const std::shared_ptr& wallet) EXCLUSIVE_LOCKS_REQUIRED(!cs_wallet_manager_map); void DoMaintenance(CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(!cs_wallet_manager_map); void Remove(const std::string& name) EXCLUSIVE_LOCKS_REQUIRED(!cs_wallet_manager_map); void Flush(const std::string& name) EXCLUSIVE_LOCKS_REQUIRED(!cs_wallet_manager_map); CCoinJoinClientManager* Get(const std::string& name) const EXCLUSIVE_LOCKS_REQUIRED(!cs_wallet_manager_map); + bool DoForClient(const std::string& name, const std::function& func) + EXCLUSIVE_LOCKS_REQUIRED(!cs_wallet_manager_map); template void ForEachCJClientMan(Callable&& func) EXCLUSIVE_LOCKS_REQUIRED(!cs_wallet_manager_map) @@ -240,7 +242,7 @@ class CCoinJoinClientQueueManager : public CCoinJoinBaseManager /** Used to keep track of current status of mixing pool */ -class CCoinJoinClientManager +class CCoinJoinClientManager : public interfaces::CoinJoin::Client { private: const std::shared_ptr m_wallet; @@ -254,8 +256,6 @@ class CCoinJoinClientManager // TODO: or map ?? std::deque deqSessions GUARDED_BY(cs_deqsessions); - std::atomic fMixing{false}; - int nCachedLastSuccessBlock{0}; int nMinBlocksToWait{1}; // how many blocks to wait for after one successful mixing tx in non-multisession mode bilingual_str strAutoDenomResult; @@ -263,15 +263,15 @@ class CCoinJoinClientManager // Keep track of current block height int nCachedBlockHeight{0}; + int nCachedNumBlocks{std::numeric_limits::max()}; // used for the overview screen + bool fCreateAutoBackups{true}; // builtin support for automatic backups + bool WaitForAnotherBlock() const; // Make sure we have enough keys since last backup bool CheckAutomaticBackup(); public: - int nCachedNumBlocks{std::numeric_limits::max()}; // used for the overview screen - bool fCreateAutoBackups{true}; // builtin support for automatic backups - CCoinJoinClientManager() = delete; CCoinJoinClientManager(const CCoinJoinClientManager&) = delete; CCoinJoinClientManager& operator=(const CCoinJoinClientManager&) = delete; @@ -283,14 +283,6 @@ class CCoinJoinClientManager void ProcessMessage(CNode& peer, CChainState& active_chainstate, CConnman& connman, const CTxMemPool& mempool, std::string_view msg_type, CDataStream& vRecv) EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions); - bool StartMixing(); - void StopMixing(); - bool IsMixing() const; - void ResetPool() EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions); - - std::vector GetStatuses() const EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions); - std::string GetSessionDenoms() EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions); - bool GetMixingMasternodesInfo(std::vector& vecDmnsRet) const EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions); /// Passively run mixing in the background according to the configuration in settings @@ -314,7 +306,18 @@ class CCoinJoinClientManager void DoMaintenance(ChainstateManager& chainman, CConnman& connman, const CTxMemPool& mempool) EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions); - void GetJsonInfo(UniValue& obj) const EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions); + // interfaces::CoinJoin::Client overrides + void resetCachedBlocks() override { nCachedNumBlocks = std::numeric_limits::max(); } + int getCachedBlocks() const override { return nCachedNumBlocks; } + void setCachedBlocks(int nCachedBlocks) override { nCachedNumBlocks = nCachedBlocks; } + void disableAutobackups() override { fCreateAutoBackups = false; } + void resetPool() override EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions); + UniValue getJsonInfo() const override EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions); + std::vector getSessionStatuses() const override EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions); + std::string getSessionDenoms() const override EXCLUSIVE_LOCKS_REQUIRED(!cs_deqsessions); + bool isMixing() const override; + bool startMixing() override; + void stopMixing() override; }; #endif // BITCOIN_COINJOIN_CLIENT_H diff --git a/src/coinjoin/coinjoin.cpp b/src/coinjoin/coinjoin.cpp index 33fbc43d6b0e..f18a991722be 100644 --- a/src/coinjoin/coinjoin.cpp +++ b/src/coinjoin/coinjoin.cpp @@ -92,7 +92,7 @@ bool CCoinJoinBroadcastTx::IsValidStructure() const if (tx->vin.size() < size_t(CoinJoin::GetMinPoolParticipants())) { return false; } - if (tx->vin.size() > CoinJoin::GetMaxPoolParticipants() * COINJOIN_ENTRY_MAX_SIZE) { + if (tx->vin.size() > CoinJoin::GetMaxPoolInputOutputCount()) { return false; } return ranges::all_of(tx->vout, [] (const auto& txOut){ diff --git a/src/coinjoin/coinjoin.h b/src/coinjoin/coinjoin.h index 6e176ebfe302..2c0fbc0ed875 100644 --- a/src/coinjoin/coinjoin.h +++ b/src/coinjoin/coinjoin.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,15 @@ static constexpr int COINJOIN_SIGNING_TIMEOUT = 15; static constexpr size_t COINJOIN_ENTRY_MAX_SIZE = 9; +namespace CoinJoin { +/// Get the minimum/maximum number of participants for the pool +int GetMinPoolParticipants(); +int GetMaxPoolParticipants(); + +/// Maximum number of inputs or outputs across a full pool +inline size_t GetMaxPoolInputOutputCount() { return size_t(GetMaxPoolParticipants()) * COINJOIN_ENTRY_MAX_SIZE; } +} // namespace CoinJoin + // pool responses enum PoolMessage : int32_t { ERR_ALREADY_HAVE, @@ -164,9 +174,25 @@ class CCoinJoinEntry { } - SERIALIZE_METHODS(CCoinJoinEntry, obj) + template + void Serialize(Stream& s) const + { + s << vecTxDSIn << txCollateral << vecTxOut; + } + + template + void Unserialize(Stream& s) { - READWRITE(obj.vecTxDSIn, obj.txCollateral, obj.vecTxOut); + const size_t max_count{CoinJoin::GetMaxPoolInputOutputCount()}; + if (!UnserializeVectorWithMaxSize(s, vecTxDSIn, max_count)) { + throw std::ios_base::failure("CCoinJoinEntry::vecTxDSIn size too large"); + } + + s >> txCollateral; + + if (!UnserializeVectorWithMaxSize(s, vecTxOut, max_count)) { + throw std::ios_base::failure("CCoinJoinEntry::vecTxOut size too large"); + } } bool AddScriptSig(const CTxIn& txin); @@ -355,10 +381,6 @@ namespace CoinJoin { bilingual_str GetMessageByID(PoolMessage nMessageID); - /// Get the minimum/maximum number of participants for the pool - int GetMinPoolParticipants(); - int GetMaxPoolParticipants(); - constexpr CAmount GetMaxPoolAmount() { return COINJOIN_ENTRY_MAX_SIZE * vecStandardDenominations.front(); } /// If the collateral is valid given by a client diff --git a/src/coinjoin/interfaces.cpp b/src/coinjoin/interfaces.cpp index 8dd645025561..0f1fb4ce859b 100644 --- a/src/coinjoin/interfaces.cpp +++ b/src/coinjoin/interfaces.cpp @@ -20,60 +20,6 @@ using wallet::CWallet; namespace coinjoin { namespace { -class CoinJoinClientImpl : public interfaces::CoinJoin::Client -{ - CCoinJoinClientManager& m_clientman; - -public: - explicit CoinJoinClientImpl(CCoinJoinClientManager& clientman) - : m_clientman(clientman) {} - - void resetCachedBlocks() override - { - m_clientman.nCachedNumBlocks = std::numeric_limits::max(); - } - void resetPool() override - { - m_clientman.ResetPool(); - } - void disableAutobackups() override - { - m_clientman.fCreateAutoBackups = false; - } - int getCachedBlocks() override - { - return m_clientman.nCachedNumBlocks; - } - void getJsonInfo(UniValue& obj) override - { - return m_clientman.GetJsonInfo(obj); - } - std::string getSessionDenoms() override - { - return m_clientman.GetSessionDenoms(); - } - std::vector getSessionStatuses() override - { - return m_clientman.GetStatuses(); - } - void setCachedBlocks(int nCachedBlocks) override - { - m_clientman.nCachedNumBlocks = nCachedBlocks; - } - bool isMixing() override - { - return m_clientman.IsMixing(); - } - bool startMixing() override - { - return m_clientman.StartMixing(); - } - void stopMixing() override - { - m_clientman.StopMixing(); - } -}; - class CoinJoinLoaderImpl : public interfaces::CoinJoin::Loader { private: @@ -82,37 +28,31 @@ class CoinJoinLoaderImpl : public interfaces::CoinJoin::Loader return *Assert(m_node.cj_walletman); } - interfaces::WalletLoader& wallet_loader() - { - return *Assert(m_node.wallet_loader); - } - public: explicit CoinJoinLoaderImpl(NodeContext& node) : m_node(node) { - // Enablement will be re-evaluated when a wallet is added or removed - CCoinJoinClientOptions::SetEnabled(false); + CCoinJoinClientOptions::SetEnabled(gArgs.GetBoolArg("-enablecoinjoin", true)); } void AddWallet(const std::shared_ptr& wallet) override { - manager().addWallet(wallet); - g_wallet_init_interface.InitCoinJoinSettings(*this, wallet_loader()); + if (!manager().addWallet(wallet) || !CCoinJoinClientOptions::IsEnabled()) return; + manager().doForClient(wallet->GetName(), [](CCoinJoinClientManager& mgr) { + g_wallet_init_interface.InitCoinJoinSettings(mgr); + }); } void RemoveWallet(const std::string& name) override { manager().removeWallet(name); - g_wallet_init_interface.InitCoinJoinSettings(*this, wallet_loader()); } void FlushWallet(const std::string& name) override { manager().flushWallet(name); } - std::unique_ptr GetClient(const std::string& name) override + bool WithClient(const std::string& name, const std::function& func) override { - auto clientman = manager().getClient(name); - return clientman ? std::make_unique(*clientman) : nullptr; + return manager().doForClient(name, [&](CCoinJoinClientManager& mgr) { func(mgr); }); } NodeContext& m_node; diff --git a/src/coinjoin/server.cpp b/src/coinjoin/server.cpp index e431e1d9632b..af31e11251fd 100644 --- a/src/coinjoin/server.cpp +++ b/src/coinjoin/server.cpp @@ -16,6 +16,7 @@ #include #include #include