Skip to content

fix: bound mnListsCache admission to stop getmnlistd memory DoS - #62

Draft
PastaPastaPasta wants to merge 231 commits into
developfrom
sec/v044
Draft

fix: bound mnListsCache admission to stop getmnlistd memory DoS#62
PastaPastaPasta wants to merge 231 commits into
developfrom
sec/v044

Conversation

@PastaPastaPasta

Copy link
Copy Markdown
Owner

Audit finding V044. An unauthenticated peer can grow CDeterministicMNManager's in-memory caches without bound via getmnlistd.

Issue

mnListsCache and mnListDiffsCache were only trimmed by CleanupCache(), which runs when a new block arrives. Between blocks there is no bound at all: a peer spamming getmnlistd for historical blocks drives GetListForBlock to append an entry per requested block, and a full mainnet MN list is several MB. No proof of work, no authentication, no rate limit on the request side.

Fix

Bound admission two ways, in src/evo/deterministicmns.{cpp,h}:

  • ShouldRetainCacheHeight() — do not retain at all any height older than the recency window CleanupCache would have dropped anyway (height + LIST_DIFFS_CACHE_SIZE >= tipIndex->nHeight). Retains freely before the tip is known, for early startup.
  • EnforceListsCacheLimit() / EnforceDiffsCacheLimit() — hard caps (MAX_CACHE_LISTS = 256, MAX_CACHE_DIFFS = LIST_DIFFS_CACHE_SIZE + 64) that evict the oldest-height entry, never the tip snapshot.

Admission is funneled through new CacheMNList() / CacheMNListDiff() helpers so no call site can bypass the bound.

MAX_CACHE_LISTS is sized well above honest steady-state usage (tip + live quorum bases + mini-snapshots within the recency window).

Tests

test: prove mnListsCache grows unboundedly via historical GetListForBlock precedes the fix and fails without it. GetListCacheSize() / GetListDiffsCacheSize() accessors expose real production state rather than adding test-only mutation hooks.

Review notes

The third commit is a review follow-up worth reading on its own: the initial fix evicted diffs during the rebuild walk, so a diff the apply loop still needed could be evicted mid-walk, landing on a bare assert(false) — a remotely-triggerable crash, i.e. worse than the DoS being fixed. The follow-up removes the mid-walk eviction, admits every diff the walk reads unconditionally, and enforces the cap once the walk is done. It also deletes the assert(false) path.

Two locking details, both deliberate:

  • CacheMNList prefers emplace over assignment because CDeterministicMNList::operator= locks m_cached_sml_mutex, which must not run while cs is held (lock-order checker).
  • All new helpers are EXCLUSIVE_LOCKS_REQUIRED(cs) with AssertLockHeld.

Based on dashpay/dash develop @ 6d04c60ef36. Not rebase-tested against any newer tip; full functional suite not run.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e6cf6c95-c50b-4a5a-ab18-e55c6b508674

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sec/v044

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Cut the functional test from 15 nodes/14 masternodes to 11 nodes/10
masternodes to lower process overhead while preserving coverage:

- llmq_test remains size 5 / threshold 3, so probe counts (==4),
  wait_for_mnauth(4), and reconnect expectations stay logically intact.
- Tighten the pre-SPORK aggregate verified-MN connection bound from 40
  to 25 so accidental broad connectivity remains distinguishable with
  the smaller masternode set.
- DIP0024 (llmq_test_dip0024 size 4, signingActiveQuorumCount 2) needs
  8 concurrent members for two cycle quorums; 10 MNs leave 2 non-members
  for inter-quorum watch-connection coverage, matching the stronger
  known-good floor used by feature_llmq_rotation (8 MNs).
@PastaPastaPasta
PastaPastaPasta force-pushed the sec/v044 branch 3 times, most recently from aa93a4c to 0340fcd Compare August 9, 2026 01:24
knst and others added 23 commits August 11, 2026 22:28
The asan job reports leaks owned by two dependencies: Berkeley DB's lock
table and memory-pool file metadata, and Qt's process-global DBus
connection manager.

For Berkeley DB, do what upstream did in 04a7a7a and stop building it in
this job. Upstream's asan job carried -DWITH_BDB=ON and libdb5.3++-dev until
that commit, ran the same tool_wallet.py and wallet_hd.py, and never needed
a Berkeley DB entry in test/sanitizer_suppressions/lsan; the file has never
contained one. BerkeleyEnvironment::Close() here is identical to the last
upstream version, so the difference is not wallet code but the dependency:
upstream linked the system libdb 5.3 shared library, while this job takes
DEP_OPTS="" and so links 4.8.30 statically out of depends. The msan job is
already configured the same way.

Qt still needs a suppression, and by symbol rather than by module. Upstream
suppresses Qt with leak:libQt6Widgets, which only works for a shared
library; with Qt linked statically from depends the allocation belongs to
the executable and that form cannot match. Upstream hit this in 5be31b2
and answered it with per-symbol rules, so add QDBusConnectionManager next to
the two symbols already taken from that commit.
…d JSON

fc892c3 rpc: Fail to return undocumented or misdocumented JSON (MarcoFalke)
f4bc4a7 rpc: Add m_skip_type_check to RPCResult (MarcoFalke)

Pull request description:

  This avoids documentation shortcomings such as the ones fixed in commit e7b6272, 138d55e, 577bd51, f8c84e0, 0ee9a00, 13f4185, or faecb2e

ACKs for top commit:
  fanquake:
    ACK fc892c3 - tested that this catches issue, i.e bitcoin#24691:

Tree-SHA512: 9d0d7e6291bfc6f67541a4ff746d374ad8751fefcff6d103d8621c0298b190ab1d209ce96cfc3a0d4a6a5460a9f9bb790eb96027b16e5ff91f2512e40c92ca84

Co-authored-by: fanquake <fanquake@gmail.com>
… JSON-RPC interface

Includes 6a1a66c rpc: render Type::ANY in help text instead of aborting

RPCResult::Type::ANY triggers NONFATAL_UNREACHABLE() in ToSections(),
which crashes the help() RPC when a command uses Type::ANY in a
nested result field.

Previously this was never hit because Type::ANY was only used as a
top-level alternate result type, filtered out before ToSections() is
called.

getopenrpcinfo() will use this result type, so render it like other
types allowing it to be used in nested result definitions like schema.

Co-authored-by: will <will@256k1.dev>
…hen renaming chainstates

Dash adaptation folded in: Discard Dash snapshot lifecycle markers only after the invalid snapshot directory rename succeeds. If the rename fails, preserving the markers keeps the existing restart recovery state recognizable while the upstream rename error is propagated to the fatal shutdown message.
…fatal error

Dash adaptation folded in: Keep Dash’s mock shutdown callback while asserting the expected fatal diagnostic. The default callback reaches StartShutdown(), whose unit-test guard aborts the process before this Dash test can complete.
a733dd7 Remove unused function `reliesOnAssumedValid` (Suhas Daftuar)
d4a11ab Cache block index entry corresponding to assumeutxo snapshot base blockhash (Suhas Daftuar)
3556b85 Move CheckBlockIndex() from Chainstate to ChainstateManager (Suhas Daftuar)
0ce805b Documentation improvements for assumeutxo (Ryan Ofsky)
768690b Fix initialization of setBlockIndexCandidates when working with multiple chainstates (Suhas Daftuar)
d43a1f1 Tighten requirements for adding elements to setBlockIndexCandidates (Suhas Daftuar)
d0d40ea Move block-storage-related logic to ChainstateManager (Suhas Daftuar)
3cfc753 test: Clear block index flags when testing snapshots (Suhas Daftuar)
272fbc3 Update CheckBlockIndex invariants for chains based on an assumeutxo snapshot (Suhas Daftuar)
10c0571 Add wrapper for adding entries to a chainstate's block index candidates (Suhas Daftuar)
471da5f Move block-arrival information / preciousblock counters to ChainstateManager (Suhas Daftuar)
1cfc887 Remove CChain dependency in node/blockstorage (Suhas Daftuar)
fe86a7c Explicitly track maximum block height stored in undo files (Suhas Daftuar)

Pull request description:

  This PR proposes a clean up of the relationship between block storage and the chainstate objects, by moving the decision of whether to store a block on disk to something that is not chainstate-specific.  Philosophically, the decision of whether to store a block on disk is related to validation rules that do not require any UTXO state; for anti-DoS reasons we were using some chainstate-specific heuristics, and those have been reworked here to achieve the proposed separation.

  This PR also fixes a bug in how a chainstate's `setBlockIndexCandidates` was being initialized; it should always have all the HAVE_DATA block index entries that have more work than the chain tip.  During startup, we were not fully populating `setBlockIndexCandidates` in some scenarios involving multiple chainstates.

  Further, this PR establishes a concept that whenever we have 2 chainstates, that we always know the snapshotted chain's base block and the base block's hash must be an element of our block index. Given that, we can establish a new invariant that the background validation chainstate only needs to consider blocks leading to that snapshotted block entry as potential candidates for its tip. As a followup I would imagine that when writing net_processing logic to download blocks for the background chainstate, that we would use this concept to only download blocks towards the snapshotted entry as well.

ACKs for top commit:
  achow101:
    ACK a733dd7
  jamesob:
    reACK a733dd7 ([`jamesob/ackr/27746.5.sdaftuar.rework_validation_logic`](https://github.com/jamesob/bitcoin/tree/ackr/27746.5.sdaftuar.rework_validation_logic))
  Sjors:
    Code review ACK a733dd7.
  ryanofsky:
    Code review ACK a733dd7. Just suggested changes since the last review. There are various small things that could be followed up on, but I think this is ready for merge.

Tree-SHA512: 9ec17746f22b9c27082743ee581b8adceb2bd322fceafa507b428bdcc3ffb8b4c6601fc61cc7bb1161f890c3d38503e8b49474da7b5ab1b1f38bda7aa8668675
Preserve ChainLock candidate exclusions in the new admission wrapper and keep Dash background-notification and EvoDB fixtures consistent with the tightened multi-chainstate candidate invariants.
Peer-penalty exemption for unavailable history hinged on three files repeating one literal string that IsBlockDataUnavailableError() then substring-matched; rewording any copy would silently revert those paths to Misbehaving. Define the suffix once next to BlockDataUnavailableError and use it at every producer and in the matcher.
Both chainstates carried a live mempool pointer after snapshot activation, so background ConnectTip called removeForBlock and removeExpiredAssetUnlock with historical blocks and lower heights. Follow the bitcoin#27596 shape: only the active chainstate keeps the mempool. Runtime activation transfers it to the snapshot chainstate, restart activation clears it from the background chainstate, and the invalid-snapshot revert hands it back.
Records why a commitment retained by UndoBlock for another chainstate's benefit cannot currently resurface as a duplicate in CQuorumBlockProcessor::ProcessCommitment.
The background chainstate in this fixture is reset to genesis before activation, so the base MN list is not derivable; a capture at activation would fabricate an empty list and poison the shared list cache (thepastaclaw review finding).
…abort

GetSnapshotBaseBlock() bypassed Chainstate::SnapshotBase() so completion could observe a missing base and return BASE_BLOCKHASH_MISMATCH, but that branch was unreachable: LoadBlockIndex's candidate admission Asserts the base for the background chainstate before completion ever runs, so a missing base aborted the node anyway, and the bypass silently lost upstream's per-call caching (bitcoin d4a11ab).

Restore the cached delegation, make SnapshotBase() non-asserting (synthetic unit fixtures activate a snapshot before its base is indexed), and detect the missing base explicitly in ChainstateManager::LoadBlockIndex() before any admission runs, failing with the standard reindex advice; -reindex already discards the snapshot chainstate and its EvoDB markers. Covered by a new test that wipes blocks/index under a persisted snapshot.
The crash-recovery state machine depends on every chainstate-directory rename and removal being followed by a DirectoryCommit of the parent, but the pattern was open-coded at six sites where the commit could silently be forgotten. Add RenameDurably/RemoveAllDurably next to DirectoryCommit and use them everywhere the snapshot lifecycle touches directories.
…idate admission

The CheckBlockIndex doc comment referenced upstream's m_options.check_block_index, which Dash does not have; the gate is still the fCheckBlockIndex global. Also document why ResetBlockFailureFlags defers candidate admission to a pass over every usable chainstate instead of upstream's inline insert (review question in dashpay#7553).
… overlap

EraseSnapshotMarkers' removal of the background MN-list hash key was untested, and the promote/discard overlap that RecoverSnapshotCleanup documents (invalid-rename crash with the SNAPSHOT marker at the background tip) relied on a comment alone. Pin both: the marker-rollback test now seeds and asserts the background key, and a new recovery test drives the overlap state through LoadVerifyActivateChainstate and asserts the promote branch lands in the same end state as a completed discard.
sendtoaddress and sendmany built a version 2 asset lock for Platform recipients unconditionally, without checking whether the v24 hard fork had activated. On a node running with -acceptnonstdtxn=1 (the only configuration where Platform sends currently relay at all), paying a Platform address before v24 activation built, signed and committed a consensus-invalid transaction (bad-assetlocktx-version-2). CommitTransaction returns void and only logs the broadcast failure, so the caller still got a txid back and the user was left with spent inputs, a permanently pending wallet entry and abandontransaction as the only way out.

Gate this the way ProTx RPCs already gate on DIP0003 in SignAndSendSpecialTx(): check activation in the RPC layer rather than in the wallet, which has never consulted deployment state. The check goes in ParseRecipients next to the other Platform-address validation, and throws RPC_INVALID_PARAMETER with wording matching the existing "requires <version>" refusals in rpc/evo_util.cpp. The accessor still has to live on interfaces::Chain because wallet RPCs have no direct ChainstateManager access.
e901404 settings: add auto-generated warning msg for editing the file manually (furszy)
966f5de init: improve corrupted/empty settings file error msg (furszy)

Pull request description:

  Small and simple issue reported [here](https://community.umbrel.com/t/bitcoin-docker-container-keeps-restarting/2144).

  Improving a confusing situation reported by users who did not understand why a
  settings parsing error occurred when the file was empty and did not know how to solve it.

  Empty setting file could be due (1) corruption or (2) an user manually cleaning up the file content.
  In both scenarios, the 'Unable to parse settings file' error does not help the user move forward.

ACKs for top commit:
  achow101:
    ACK e901404
  hebasto:
    re-ACK e901404.
  ryanofsky:
    Code review ACK e901404. Just whitespace formatting changes and shortening a test string literal since last review
  shaavan:
    Code review ACK e901404

Tree-SHA512: 2910654c6b9e9112de391eedb8e46980280f822fa3059724dd278db7436804dd27fae628d2003f2c6ac1599b07ac5c589af016be693486e949f558515e662bec
8f6ab31 init: don't delete PID file if it was not generated (willcl-ark)

Pull request description:

  In a similar vein to bitcoin#28784, if a second `bitcoind` is started using the same datadir it will fail to start up, but during shutdown remove the PID file from the first `bitcoind` instance.

ACKs for top commit:
  achow101:
    ACK 8f6ab31
  andrewtoth:
    ACK 8f6ab31
  romanz:
    ACK bitcoin@8f6ab31

Tree-SHA512: c9af703cbfa179d33ef9580a51e86c1b0acbd28daa18c8d2e5e5ff796ab4d3e2009a962a47e6046a0e5ece936f8a06ee8af5fdf8ff4ae1e52cbcdbec4b942271
…n and backup requirement

ca09415 rpc, doc: encryptwallet, mention HD seed rotation and new backup (furszy)

Pull request description:

  Small and simple PR, updating the `encryptwallet` help message.

  Better to notify users about the HD seed rotation and the new
  backup requirement before executing the encryption process.
  Ensuring they are prepared to update previous backups and
  securely safeguard the updated wallet file.

ACKs for top commit:
  S3RK:
    ACK ca09415
  achow101:
    ACK ca09415

Tree-SHA512: f0ee65f5cea66450566e3a85e066d4c06b3293dd0e0b2ed5fafdb7fb11da0a2cd94407299a3c57a0706c2ed782f8eabb73443e85d8099a62a3fb10a02636ab46
…s passed to getnetworkhashps RPC

9ac114e Throw error if invalid parameters passed to getnetworkhashps RPC endpoint (Jameson Lopp)

Pull request description:

  When writing some scripts that iterated over many blocks to generate hashrate estimates I realized that my script was going out of range of the current chain tip height but was not encountering any errors.

  I believe that passing an invalid block height to this function but receiving the hashrate estimate for the chain tip instead should be considered unexpected behavior.

ACKs for top commit:
  Sjors:
    re-utACK 9ac114e
  kevkevinpal:
    reACK [9ac114e](bitcoin@9ac114e)
  achow101:
    ACK 9ac114e

Tree-SHA512: eefb465c2dd654fc48267f444e1809597ec5363cdd131ea9ec812458fed1e4bffbbbb0617d74687c9f7bb16274b598d8292f5eeb7953421e5d2a8dc2cc081f2b
…alletprocesspsbt if complete

2e249b9 doc: add release note for PR bitcoin#28414 (Matthew Zipkin)
4614332 test: remove unnecessary finalizepsbt rpc calls (ismaelsadeeq)
e3d484b wallet rpc: return final tx hex from walletprocesspsbt if complete (Matthew Zipkin)

Pull request description:

  See bitcoin#28363 (comment)

  `walletprocesspsbt` currently returns a base64-encoded PSBT and a boolean indicating if the tx is "complete". If it is complete, the base64 PSBT can be finalized with `finalizepsbt` which returns the hex-encoded transaction suitable for `sendrawtransaction`.

  With this patch, `walletprocesspsbt` return object will ALSO include the broadcast-able hex string if the tx is already final. This saves users the extra step of calling `finalizepsbt` assuming they have already inspected and approve the transaction from earlier steps.

ACKs for top commit:
  ismaelsadeeq:
    re ACK 2e249b9
  BrandonOdiwuor:
    re ACK 2e249b9
  Randy808:
    Tested ACK 2e249b9
  achow101:
    ACK 2e249b9
  ishaanam:
    ACK 2e249b9

Tree-SHA512: 229c1103265a9b4248f080935a7ad5607c3be3f9a096a9ab6554093b2cd8aa8b4d1fa55b1b97d3925ba208dbc3ccba4e4d37c40e1491db0d27ba3d9fe98f931e
PastaPastaPasta and others added 29 commits August 20, 2026 08:31
…ce and fallback

bf9f4a4 test(qt): cover transaction type filter persistence and fallback (pasta)

Pull request description:

  ## Issue being fixed or feature implemented

  Following up on dashpay#7595 where the transaction type filter persistence fix was merged, this adds regression test coverage in `ProviderTransactionTests` without duplicating test fixtures.

  ## What was done?

  - Move transaction type filter persistence and fallback test cases into `src/qt/test/providertransactiontests.cpp`.
  - Add test coverage for `Dust Receive` and `Other` filter restoration.
  - Add test coverage for fallback to "Most Common" when restoring hidden CoinJoin filters (with CoinJoin disabled) or unknown filters upon wallet model attach.
  - Add test coverage for default selection when no filter setting is stored.

  ## How Has This Been Tested?

  - Ran `src/qt/test/test_dash-qt` (both minimal and cocoa platform).
  - Ran static linters (`test/lint/all-lint.py`).
  - Ran core unit tests (`src/test/test_dash`).

  ## Breaking Changes

  None.

  ## Checklist:

  - [x] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [ ] I have assigned this pull request to a milestone

Top commit has no ACKs.

Tree-SHA512: 0badaedfa0a3752c2ed05af239f97c789d0ad5e1a3b9703e9c7003304e5633e8958721feb410744bfcc61b73b9e9dea9cd8125834a2dde68b69eb6fd4c599859
…ance UI

297c15b docs: add masternode management release notes (pasta)
cebae5a feat(qt): add masternode registration and maintenance UI (pasta)

Pull request description:

  ## Issue being fixed or feature implemented

  The Masternodes tab is currently read-only, so registering or maintaining a
  regular masternode or evonode requires operators to drive multi-step `protx`
  workflows from the debug console. This adds an action-oriented Qt workflow on
  top of the typed provider-transaction interface.

  This is the focused regular/Evo extraction and refinement of the corresponding
  work in [#68](#68).
  Shared-masternode UI remains separate.

  The supporting foundations are already merged in
  [dash#7595](dashpay#7595),
  [dash#7600](dashpay#7600), and
  [dash#7616](dashpay#7616).
  [dash#7594](dashpay#7594) is explicitly **not** a
  dependency: this PR does not derive, reserve, recover, record, or later reveal
  operator keys from the wallet seed.

  ## What was done?

  - Added a regular/Evo registration wizard to the Masternodes tab.
    - Supports wallet-funded collateral, an exact wallet-owned collateral UTXO,
      and external/hardware-held collateral through prepare/sign/submit.
    - Supports either a freshly generated basic-scheme operator key, shown and
      suffix-confirmed before registration can begin, or an externally supplied
      operator public key.
    - Handles regular and Evo service fields, including v24 extended Core and
      Platform endpoint lists.
    - Presents a complete review, keeps synchronous wallet/provider work off the
      GUI thread, protects prepared collateral locks, and blocks unsafe close/back
      behavior while operations are active.
  - Added role-aware maintenance actions to each masternode row.
    - Update Service supports regular and Evo fields, operator authentication,
      optional operator payout, and automatic or explicit fee funding.
    - Update Registrar preserves unchanged values, requires the owner key, avoids
      flattening multi-payout registrations, and warns about the PoSe consequence
      of rotating the operator key.
    - Revoke exposes all four DIP3 reasons and clearly explains that collateral
      is unaffected.
  - Added deterministic Qt coverage for endpoint validation, fee-source
    selection, request construction, key handling, model reconciliation,
    threading, dialog/wizard lifecycle, and no-wallet behavior.

  ## Screenshots

  Native macOS captures from the exact current head `297c15bbe01862669559ee179753b9088b383a7e` using disposable post-v24 regtest fixtures. Each theme independently exercises the complete 36-state walkthrough: 26 registration states, 3 blocking-validation states, and 7 maintenance states. Fresh wallet clones mean wallet-generated role-address choices and values, operator keys, and transaction hashes can differ between Dark and Light runs; fixed collateral outpoints, manually entered endpoints, Platform node IDs, and external-role inputs are held constant.

  The delayed final broadcast confirmation is also [captured separately](https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-supplemental-confirm-before-broadcast.png). All originals and `SHA256SUMS` are published in the [verified evidence release](https://github.com/PastaPastaPasta/dash/releases/tag/ui-artifacts-297c15bb).

  <details open>
  <summary><strong>Dark mode — complete 36-image walkthrough</strong></summary>

  ### Registration — regular, Evo, and external collateral

  <table>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-01-masternodes-empty-entry.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-01-masternodes-empty-entry.png" alt="dark-registration-01-masternodes-empty-entry" width="360"></a><br><sub>Masternodes Empty Entry</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-02-type-regular.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-02-type-regular.png" alt="dark-registration-02-type-regular" width="360"></a><br><sub>Type Regular</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-03-collateral-wallet-funded.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-03-collateral-wallet-funded.png" alt="dark-registration-03-collateral-wallet-funded" width="360"></a><br><sub>Collateral Wallet Funded</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-04-collateral-existing.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-04-collateral-existing.png" alt="dark-registration-04-collateral-existing" width="360"></a><br><sub>Collateral Existing</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-05-service-regular-optional.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-05-service-regular-optional.png" alt="dark-registration-05-service-regular-optional" width="360"></a><br><sub>Service Regular Optional</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-06-keys-generated-no-derivation.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-06-keys-generated-no-derivation.png" alt="dark-registration-06-keys-generated-no-derivation" width="360"></a><br><sub>Keys Generated No Derivation</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-07-payout.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-07-payout.png" alt="dark-registration-07-payout" width="360"></a><br><sub>Payout</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-08-fee-source.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-08-fee-source.png" alt="dark-registration-08-fee-source" width="360"></a><br><sub>Fee Source</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-09-review-regular.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-09-review-regular.png" alt="dark-registration-09-review-regular" width="360"></a><br><sub>Review Regular</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-10-wallet-unlock.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-10-wallet-unlock.png" alt="dark-registration-10-wallet-unlock" width="360"></a><br><sub>Wallet Unlock</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-11-save-operator-key-before-registration.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-11-save-operator-key-before-registration.png" alt="dark-registration-11-save-operator-key-before-registration" width="360"></a><br><sub>Save Operator Key Before Registration</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-12-operator-key-confirmed-before-registration.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-12-operator-key-confirmed-before-registration.png" alt="dark-registration-12-operator-key-confirmed-before-registration" width="360"></a><br><sub>Operator Key Confirmed Before Registration</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-13-confirmed-regular-row.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-13-confirmed-regular-row.png" alt="dark-registration-13-confirmed-regular-row" width="360"></a><br><sub>Confirmed Regular Row</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-14-type-evo.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-14-type-evo.png" alt="dark-registration-14-type-evo" width="360"></a><br><sub>Type Evo</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-15-evo-existing-4000-collateral.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-15-evo-existing-4000-collateral.png" alt="dark-registration-15-evo-existing-4000-collateral" width="360"></a><br><sub>Evo Existing 4000 Collateral</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-16-evo-core-service.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-16-evo-core-service.png" alt="dark-registration-16-evo-core-service" width="360"></a><br><sub>Evo Core Service</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-17-evo-platform-valid-placeholder.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-17-evo-platform-valid-placeholder.png" alt="dark-registration-17-evo-platform-valid-placeholder" width="360"></a><br><sub>Evo Platform Valid Placeholder</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-18-evo-platform-complete.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-18-evo-platform-complete.png" alt="dark-registration-18-evo-platform-complete" width="360"></a><br><sub>Evo Platform Complete</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-19-review-evo-extended-services.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-19-review-evo-extended-services.png" alt="dark-registration-19-review-evo-extended-services" width="360"></a><br><sub>Review Evo Extended Services</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-20-result-evo.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-20-result-evo.png" alt="dark-registration-20-result-evo" width="360"></a><br><sub>Result Evo</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-21-external-collateral.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-21-external-collateral.png" alt="dark-registration-21-external-collateral" width="360"></a><br><sub>External Collateral</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-22-external-operator-public-key.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-22-external-operator-public-key.png" alt="dark-registration-22-external-operator-public-key" width="360"></a><br><sub>External Operator Public Key</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-23-review-external-collateral.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-23-review-external-collateral.png" alt="dark-registration-23-review-external-collateral" width="360"></a><br><sub>Review External Collateral</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-24-external-sign-message.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-24-external-sign-message.png" alt="dark-registration-24-external-sign-message" width="360"></a><br><sub>External Sign Message</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-25-valid-external-signature.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-25-valid-external-signature.png" alt="dark-registration-25-valid-external-signature" width="360"></a><br><sub>Valid External Signature</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-26-result-external-no-secret.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-registration-26-result-external-no-secret.png" alt="dark-registration-26-result-external-no-secret" width="360"></a><br><sub>Result External No Secret</sub></td>
  </tr>
  </table>

  ### Blocking validation states

  <table>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-validation-01-wrong-secret-confirmation.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-validation-01-wrong-secret-confirmation.png" alt="dark-validation-01-wrong-secret-confirmation" width="360"></a><br><sub>Wrong Secret Confirmation</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-validation-02-evo-incomplete-platform-pair.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-validation-02-evo-incomplete-platform-pair.png" alt="dark-validation-02-evo-incomplete-platform-pair" width="360"></a><br><sub>Evo Incomplete Platform Pair</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-validation-03-invalid-external-signature.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-validation-03-invalid-external-signature.png" alt="dark-validation-03-invalid-external-signature" width="360"></a><br><sub>Invalid External Signature</sub></td>
  <td></td>
  </tr>
  </table>

  ### Maintenance — Update Service, Update Registrar, and Revoke

  <table>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-maintenance-01-update-service-current-values.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-maintenance-01-update-service-current-values.png" alt="dark-maintenance-01-update-service-current-values" width="360"></a><br><sub>Update Service Current Values</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-maintenance-02-update-service-invalid-operator-key.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-maintenance-02-update-service-invalid-operator-key.png" alt="dark-maintenance-02-update-service-invalid-operator-key" width="360"></a><br><sub>Update Service Invalid Operator Key</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-maintenance-03-update-service-valid-automatic-fee.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-maintenance-03-update-service-valid-automatic-fee.png" alt="dark-maintenance-03-update-service-valid-automatic-fee" width="360"></a><br><sub>Update Service Valid Automatic Fee</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-maintenance-04-update-registrar-current-values.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-maintenance-04-update-registrar-current-values.png" alt="dark-maintenance-04-update-registrar-current-values" width="360"></a><br><sub>Update Registrar Current Values</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-maintenance-05-update-registrar-payout-change.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-maintenance-05-update-registrar-payout-change.png" alt="dark-maintenance-05-update-registrar-payout-change" width="360"></a><br><sub>Update Registrar Payout Change</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-maintenance-06-revoke-default.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-maintenance-06-revoke-default.png" alt="dark-maintenance-06-revoke-default" width="360"></a><br><sub>Revoke Default</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-maintenance-07-revoke-valid-operator-key.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/dark-maintenance-07-revoke-valid-operator-key.png" alt="dark-maintenance-07-revoke-valid-operator-key" width="360"></a><br><sub>Revoke Valid Operator Key</sub></td>
  <td></td>
  </tr>
  </table>

  </details>

  <details>
  <summary><strong>Light mode — complete 36-image walkthrough</strong></summary>

  ### Registration — regular, Evo, and external collateral

  <table>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-01-masternodes-empty-entry.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-01-masternodes-empty-entry.png" alt="light-registration-01-masternodes-empty-entry" width="360"></a><br><sub>Masternodes Empty Entry</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-02-type-regular.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-02-type-regular.png" alt="light-registration-02-type-regular" width="360"></a><br><sub>Type Regular</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-03-collateral-wallet-funded.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-03-collateral-wallet-funded.png" alt="light-registration-03-collateral-wallet-funded" width="360"></a><br><sub>Collateral Wallet Funded</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-04-collateral-existing.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-04-collateral-existing.png" alt="light-registration-04-collateral-existing" width="360"></a><br><sub>Collateral Existing</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-05-service-regular-optional.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-05-service-regular-optional.png" alt="light-registration-05-service-regular-optional" width="360"></a><br><sub>Service Regular Optional</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-06-keys-generated-no-derivation.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-06-keys-generated-no-derivation.png" alt="light-registration-06-keys-generated-no-derivation" width="360"></a><br><sub>Keys Generated No Derivation</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-07-payout.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-07-payout.png" alt="light-registration-07-payout" width="360"></a><br><sub>Payout</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-08-fee-source.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-08-fee-source.png" alt="light-registration-08-fee-source" width="360"></a><br><sub>Fee Source</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-09-review-regular.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-09-review-regular.png" alt="light-registration-09-review-regular" width="360"></a><br><sub>Review Regular</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-10-wallet-unlock.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-10-wallet-unlock.png" alt="light-registration-10-wallet-unlock" width="360"></a><br><sub>Wallet Unlock</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-11-save-operator-key-before-registration.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-11-save-operator-key-before-registration.png" alt="light-registration-11-save-operator-key-before-registration" width="360"></a><br><sub>Save Operator Key Before Registration</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-12-operator-key-confirmed-before-registration.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-12-operator-key-confirmed-before-registration.png" alt="light-registration-12-operator-key-confirmed-before-registration" width="360"></a><br><sub>Operator Key Confirmed Before Registration</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-13-confirmed-regular-row.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-13-confirmed-regular-row.png" alt="light-registration-13-confirmed-regular-row" width="360"></a><br><sub>Confirmed Regular Row</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-14-type-evo.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-14-type-evo.png" alt="light-registration-14-type-evo" width="360"></a><br><sub>Type Evo</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-15-evo-existing-4000-collateral.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-15-evo-existing-4000-collateral.png" alt="light-registration-15-evo-existing-4000-collateral" width="360"></a><br><sub>Evo Existing 4000 Collateral</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-16-evo-core-service.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-16-evo-core-service.png" alt="light-registration-16-evo-core-service" width="360"></a><br><sub>Evo Core Service</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-17-evo-platform-valid-placeholder.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-17-evo-platform-valid-placeholder.png" alt="light-registration-17-evo-platform-valid-placeholder" width="360"></a><br><sub>Evo Platform Valid Placeholder</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-18-evo-platform-complete.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-18-evo-platform-complete.png" alt="light-registration-18-evo-platform-complete" width="360"></a><br><sub>Evo Platform Complete</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-19-review-evo-extended-services.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-19-review-evo-extended-services.png" alt="light-registration-19-review-evo-extended-services" width="360"></a><br><sub>Review Evo Extended Services</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-20-result-evo.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-20-result-evo.png" alt="light-registration-20-result-evo" width="360"></a><br><sub>Result Evo</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-21-external-collateral.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-21-external-collateral.png" alt="light-registration-21-external-collateral" width="360"></a><br><sub>External Collateral</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-22-external-operator-public-key.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-22-external-operator-public-key.png" alt="light-registration-22-external-operator-public-key" width="360"></a><br><sub>External Operator Public Key</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-23-review-external-collateral.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-23-review-external-collateral.png" alt="light-registration-23-review-external-collateral" width="360"></a><br><sub>Review External Collateral</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-24-external-sign-message.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-24-external-sign-message.png" alt="light-registration-24-external-sign-message" width="360"></a><br><sub>External Sign Message</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-25-valid-external-signature.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-25-valid-external-signature.png" alt="light-registration-25-valid-external-signature" width="360"></a><br><sub>Valid External Signature</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-26-result-external-no-secret.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-registration-26-result-external-no-secret.png" alt="light-registration-26-result-external-no-secret" width="360"></a><br><sub>Result External No Secret</sub></td>
  </tr>
  </table>

  ### Blocking validation states

  <table>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-validation-01-wrong-secret-confirmation.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-validation-01-wrong-secret-confirmation.png" alt="light-validation-01-wrong-secret-confirmation" width="360"></a><br><sub>Wrong Secret Confirmation</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-validation-02-evo-incomplete-platform-pair.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-validation-02-evo-incomplete-platform-pair.png" alt="light-validation-02-evo-incomplete-platform-pair" width="360"></a><br><sub>Evo Incomplete Platform Pair</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-validation-03-invalid-external-signature.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-validation-03-invalid-external-signature.png" alt="light-validation-03-invalid-external-signature" width="360"></a><br><sub>Invalid External Signature</sub></td>
  <td></td>
  </tr>
  </table>

  ### Maintenance — Update Service, Update Registrar, and Revoke

  <table>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-maintenance-01-update-service-current-values.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-maintenance-01-update-service-current-values.png" alt="light-maintenance-01-update-service-current-values" width="360"></a><br><sub>Update Service Current Values</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-maintenance-02-update-service-invalid-operator-key.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-maintenance-02-update-service-invalid-operator-key.png" alt="light-maintenance-02-update-service-invalid-operator-key" width="360"></a><br><sub>Update Service Invalid Operator Key</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-maintenance-03-update-service-valid-automatic-fee.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-maintenance-03-update-service-valid-automatic-fee.png" alt="light-maintenance-03-update-service-valid-automatic-fee" width="360"></a><br><sub>Update Service Valid Automatic Fee</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-maintenance-04-update-registrar-current-values.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-maintenance-04-update-registrar-current-values.png" alt="light-maintenance-04-update-registrar-current-values" width="360"></a><br><sub>Update Registrar Current Values</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-maintenance-05-update-registrar-payout-change.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-maintenance-05-update-registrar-payout-change.png" alt="light-maintenance-05-update-registrar-payout-change" width="360"></a><br><sub>Update Registrar Payout Change</sub></td>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-maintenance-06-revoke-default.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-maintenance-06-revoke-default.png" alt="light-maintenance-06-revoke-default" width="360"></a><br><sub>Revoke Default</sub></td>
  </tr>
  <tr>
  <td align="center"><a href="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-maintenance-07-revoke-valid-operator-key.png"><img src="https://github.com/PastaPastaPasta/dash/releases/download/ui-artifacts-297c15bb/light-maintenance-07-revoke-valid-operator-key.png" alt="light-maintenance-07-revoke-valid-operator-key" width="360"></a><br><sub>Revoke Valid Operator Key</sub></td>
  <td></td>
  </tr>
  </table>

  </details>
  ## How Has This Been Tested?

  Local environment: macOS arm64, Qt 5.15.18, depends-backed build.

  - `make -j8`
  - `QT_QPA_PLATFORM=cocoa ./src/qt/test/test_dash-qt`
    - all Qt suites passed
    - `MasternodeWidgetTests`: 37 passed
    - `MasternodeMaintenanceTests`: 11 passed
  - `QT_QPA_PLATFORM=cocoa make -j8 check`
    - complete unit/check suite passed
  - `test/lint/all-lint.py`
    - exited successfully; Python lint was skipped because `flake8` is not
      installed locally, and the repository's existing codespell warnings remain
  - Final wait-cursor teardown fix at `6266b98656fa1a3081503068d23205cf9b9492e0`:
    full build, full Qt test binary, `git diff --check`, whitespace lint, Dash
    cppcheck, and clang-format-diff all passed
  - Separate `--disable-wallet --with-gui=qt5` build during implementation:
    `make -C src -j8 qt/dash-qt`
  - Native UI journeys against disposable post-v24 regtest fixtures:
    - wallet-funded regular registration broadcast and confirmed in the list
    - exact-collateral Evo registration broadcast successfully
    - external-collateral prepare, invalid/valid signature handling, and submit
      completed successfully
    - Update Service, Update Registrar, and Revoke dialogs exercised with current
      values, edited values, invalid/valid operator credentials, owner-role
      gating, fee-source choices, cancel behavior, and reasons 0 through 3
  - Every retained screenshot was reopened at original resolution and checked for
    clipping, stale pre-fix content, personal data, and non-regtest secrets.

  Live testing found and fixed two issues before publication: payout shares now
  use the protocol's full 10000-unit reward, and the Evo HTTPS example uses a DNS
  name for the port-443 form accepted by provider-network validation.

  ## Breaking Changes

  None. The existing RPC interface and read-only list behavior remain available.
  The new write paths use the already-merged typed provider-transaction service.

  ## Checklist:

  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [x] I have made corresponding changes to the documentation
  - [ ] I have assigned this pull request to a milestone

  This pull request was created by Codex.

Top commit has no ACKs.

Tree-SHA512: 3a99af8d49b6f6bad93c680c76f0952f7e234c6686feb3f98287077ae5f0cd17ffef4d91cfb4dfc8c27ae3da97b2bea7e2077a6fc5a3d09dac8cc4523cf62927
3b23f95 ci: limit max stack size to 512 KiB (dergoegge)
2931a87 ci: limit stack size to 512kb in native macOS jobs (fanquake)

Partial: only the asan target and only the functional test stage.

Upstream sets CI_LIMIT_STACK_SIZE in ci/test/03_test_script.sh, which Dash
does not have; the equivalent point in Dash's flow is
ci/dash/test_integrationtests.sh, after the build and before test_runner.
Upstream also sets the variable for the arm, msan, tsan and
previous_releases targets. Those are green today and unit tests are not
where the job runs out of memory, so leave them alone rather than perturb
a passing configuration.

Upstream's 512 KiB does not work for Dash: dashd dies with SIGSEGV during
test framework startup. 1024 KiB is the smallest value tested that starts
cleanly (512 fails, 1024, 2048 and 4096 all pass).

Taken here for its effect on memory rather than its original purpose. ASan
sizes a thread's fake stack from the stack rlimit, and the threads that
actually run fault theirs in, so lowering the limit cuts resident memory
substantially. Peak resident set of all dashd nodes over
feature_llmq_data_recovery, feature_llmq_rotation, p2p_instantsend and
p2p_quorum_data at -j4:

  8 MiB (default)  14335 MiB
  2048 KiB         12912 MiB
  1024 KiB         10657 MiB

Thread count is not what drives this. The same run with -par=4 -parbls=4,
which takes each node from 15 script and 23 BLS worker threads down to 3
and 3, peaked at 14375 MiB, unchanged. Idle workers never fault their
stacks in.
48aae2c gui: Add File > Migrate Wallet (Andrew Chow)
577be88 gui: Optionally return passphrase after unlocking (Andrew Chow)
5b3a85b interfaces, wallet: Expose migrate wallet (Andrew Chow)

Pull request description:

  GUI users need to be able to migrate wallets without going to the RPC console.

ACKs for top commit:
  jarolrod:
    ACK 48aae2c
  pablomartin4btc:
    tACK 48aae2c
  hebasto:
    ACK 48aae2c

Tree-SHA512: 2d02b1e85e7d6cfbf503f417f150cdaa0c63822942e9a6fe28c0ad3e7f40a957bb01a375c909a60432dc600e84574881aa446c7ec983b56f0bb23f07ef15de54

Co-authored-by: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com>
17554ef test: prefer sqlite for wallet tests (S3RK)
8e0faba test: make wallet_migration.py pass with both wallet flags (S3RK)

Pull request description:

  Fixes bitcoin#26511

ACKs for top commit:
  MarcoFalke:
    review ACK 17554ef
  achow101:
    ACK 17554ef

Tree-SHA512: 97cae275998f07032feffe1b533d4747b8ff03c3c1fb830af69ee38cadb75fd58532956f66f79c0d275b00620ce53b0b5240f885e4f29b8bd4d0b6e6cbc683fa

Co-authored-by: Andrew Chow <github@achow101.com>
… dir

a1e6538 test: Add test for migrating default wallet and plain file wallet (Andrew Chow)
bdbe3fd wallet: Generated migrated wallet's path from walletdir and name (Andrew Chow)

Pull request description:

  This PR fixes an assertion error that is hit during the setup of the new database during migration of a wallet that was not contained in a wallet dir. Also added a test for this case as well as one for migrating the default wallet.

ACKs for top commit:
  ryanofsky:
    Code review ACK a1e6538
  furszy:
    ACK a1e6538

Tree-SHA512: 96b218c0de8567d8650ec96e1bf58b0f8ca4c4726f5efc6362453979b56b9d569baea0bb09befb3a5aed8d16d29bf75ed5cd8ffc432bbd4cbcad3ac5574bc479

Co-authored-by: Ryan Ofsky <ryan@ofsky.org>
…4, (partial) 28414

f99d9c3 (Partial) Merge bitcoin#28414: wallet rpc: return final tx hex from walletprocesspsbt if complete (Andrew Chow)
19cc3ec Merge bitcoin#28554: bugfix: throw an error if an invalid parameter is passed to getnetworkhashps RPC (Andrew Chow)
b1eacde Merge bitcoin#28980: rpc: encryptwallet help, mention HD seed rotation and backup requirement (Andrew Chow)
45a3ab2 Merge bitcoin#28946: init: don't delete PID file if it was not generated (Andrew Chow)
229e18b Merge bitcoin#29144: init: handle empty settings file gracefully (Ava Chow)

Pull request description:

  bitcoin back ports

ACKs for top commit:
  knst:
    utACK f99d9c3

Tree-SHA512: e1b25295be4a19fc3f05835cc5e7110519eefb1bfc037d2f5c17c3391765710d3e683e57c9cad0b05ad22bb88d61dfeab61938dda862e62651f11a559d646157
…ion state per package

4886b9f chore: address review feedback (pasta)
90b8a3b fix(miner): preserve failed package streak (pasta)
109c8f2 docs: add release note for dashpay#7570 (pasta)
4f666b9 fix(miner): validate special tx packages atomically (pasta)

Pull request description:

  ## Issue being fixed or feature implemented

  - Block template construction accounted for credit-pool and EHF state only for the package root before discovering and sorting its unconfirmed ancestors.
  - A prioritized child could therefore hide an over-limit AssetUnlock transaction in its ancestor set. The invalid package would be added to the candidate block and the final credit-pool replay would abort getblocktemplate instead of skipping the package.
  - Stateful checks also ran before fee, size, finality, and InstantSend gates, so a later-rejected candidate could contaminate accounting for subsequent packages.

  This pull request is based directly on develop and does not depend on another pull request.

  ## What was done?

  - Move cumulative special-transaction accounting after all non-mutating package checks and topological sorting.
  - Replay every AssetLock and AssetUnlock member in block order through a package-atomic credit-pool operation that restores amounts and indexes on failure.
  - Trial EHF signal updates on a package-local copy and commit them only after credit-pool validation succeeds.
  - Remove miner dependencies that were used only by the former root-only AssetUnlock check.
  - Add unit coverage for mid-package amount and index rollback.
  - Add functional coverage proving that a valid AssetUnlock ancestor package is mined while an over-limit ancestor package is excluded without aborting template construction.

  ## How Has This Been Tested?

  - Built dashd and test_dash locally on macOS arm64 using depends.
  - Ran evo_assetlocks_tests/credit_pool_package_atomicity.
  - Ran feature_asset_locks.py end to end. The new test fails on vulnerable code with failed-creditpool-unlock-too-much and passes with this change.
  - Ran the targeted Python lint and mypy checks for feature_asset_locks.py.
  - Ran lint-whitespace.py and git diff --check.
  - Performed an independent adversarial review of package ordering, rollback behavior, modified-entry handling, and tracker commit points.

  ## Breaking Changes

  None. Consensus validation and transaction serialization are unchanged; this changes block-template package selection so invalid packages are skipped instead of poisoning or aborting template construction.

  ## Checklist:

  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [x] I have made corresponding changes to the documentation
  - [ ] I have assigned this pull request to a milestone

  This pull request was created by Codex.

ACKs for top commit:
  knst:
    utACK 4886b9f

Tree-SHA512: f4aa470dfaea4ed33a2cb72ca1b52c7fd2e30f632b40a1e1c1ef25e9d34e08ea3fc105fa001278fb198813d361f0d90383f17de5a2460781fdf165116208c7e3
0ec62ed fix(llmq): bound QGETDATA request tracking (pasta)

Pull request description:

  ## Issue being fixed or feature implemented

  Inbound QGETDATA registration happened before validation, and each fresh `quorumHash` produced a distinct map key. Entries remained for the expiry window while cleanup depended on block-tip callbacks that are skipped during IBD and while unsynced, allowing an MNAuth peer or unauthenticated qwatch peer to grow request tracking without a hard bound.

  This PR is now the request-tracking half of the original combined change. Requester-supplied response-error validation was split into dashpay#7605 so the two defects can be reviewed and merged independently.

  ## What was done?

  - Prevalidate the LLMQ type and active-chain block before tracking:
    - unregistered LLMQ types return `QUORUM_TYPE_INVALID`, score 100, and are not tracked;
    - unknown or off-active-chain blocks return `QUORUM_BLOCK_NOT_FOUND` unscored and are not tracked;
    - active-chain blocks are registered before the commitment lookup so repeated uncached misses are rate-limited.
  - Bound attacker-controlled inbound tracking to 64 live entries per requester and 4096 globally. Outbound requests initiated by this node do not consume those budgets.
  - Score authenticated requesters that exhaust their own budget by 25. Shared/global exhaustion is not scored because the arriving peer is not necessarily responsible.
  - Run expired-request cleanup every minute independently of block-tip progress, while retaining tip-triggered cleanup.

  Unauthenticated qwatch peers continue to share the null identity used by the existing per-key limiter. Exhaustion of that shared 64-entry budget is therefore unscored; the independent 4096-entry global cap remains the hard memory bound.

  ## How Has This Been Tested?

  - Full local `make -j13` build on macOS arm64 using the prebuilt depends tree.
  - `./src/test/test_dash --run_test=llmq_qgetdata_tests`
  - `./src/test/test_dash --run_test='llmq_*'` (75 cases)
  - `/opt/homebrew/bin/python3.9 test/functional/test_runner.py p2p_quorum_data.py`
  - `test/lint/all-lint.py`
  - `git diff --check`

  The Python lint subcheck skipped because `flake8` is not installed; the remaining lint checks passed.

  ## Breaking Changes

  None.

  ## Checklist:

  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [ ] I have assigned this pull request to a milestone

  This pull request was created by Codex.

Top commit has no ACKs.

Tree-SHA512: e6bf07f0ff9b0c161e6d0cf5700a97055849431e2712b2410edb517ac39b1340d41e940b00c8ee5174e9bdbdbc521e3ab3f0e2048c2a210068c3264836dcb9ae
…jobs at the same time

e357eb1 test: use 4 jobs for asan (Konstantin Akimov)
1758709 partial Merge bitcoin#33079: ci: limit max stack size to 512 KiB (Konstantin Akimov)

Pull request description:

  ## Issue being fixed or feature implemented
  Having 4jobs for asan causes functional tests to be aborted without an explanation on CI in logs.
  Most likely it's caused by short memory and amount of jobs has been reduced to only 2 jobs.

  ## What was done?
  Backport bitcoin#33079 partially + increased amount of jobs

  ## How Has This Been Tested?
  Local tests shown that RAM used significantly reduced if 33079 included:

        8 MiB (default)  14335 MiB
        2048 KiB         12912 MiB
        1024 KiB         10657 MiB

  Probably it will let to run more than 2 jobs at once.

  ## Breaking Changes
  N/A

  ## Checklist:
  - [ ] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [ ] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

Top commit has no ACKs.

Tree-SHA512: 3231e513db78392ad53d94d97a4a2c88b66f568c42bbed3ab962216f7c90bc5fc193bebcb6fd0bbba59892b0e0117ae9dac2c618bbb0a4285247bb82badbc7c8
…d fundrawtransaction

99a818c feat: add use_cj feature to RPCs send, sendall and fundrawtransaction (Konstantin Akimov)

Pull request description:

  ## Issue being fixed or feature implemented
  The RPCs `send`, `sendall`, `fundrawtransaction` doesn't have a functionality to use cj to fund transaction.

  ## What was done?
  Added flag use_cj to the options and use it coin-select.

  ## How Has This Been Tested?
  Call RPC `send '[{"yRgziYdxyApbFsvph89JAVMzjsv8en16bA":0.01}]' null "unset" null '{"use_cj":true}'`

  It produced tx `getrawtransaction 8df1998514388b9c19705077e743c5bc2fd6dd378a6287cd865f0a1b91e8afaf 1` which looks as expected.

  ## Breaking Changes
  N/A

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [ ] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [x] I have assigned this pull request to a milestone

Top commit has no ACKs.

Tree-SHA512: aa4e6fe2a6040ffcfd1b238665cfebb2d6f2245796123a80a218dd7cc98514f2147739a2b57353f6709a4f4eda421ea3417ee2a16c02a823fab98e62252dd35c
56a61c8 Merge bitcoin#26740: wallet: Migrate wallets that are not in a wallet dir (Konstantin Akimov)
493f633 fix: follow-up backporting bitcoin#17261 - missing break (Konstantin Akimov)
087c9be Merge bitcoin#26638: test: prefer sqlite for wallet tests (Konstantin Akimov)
117dab2 Merge bitcoin-core/gui#738: Add menu option to migrate a wallet (Konstantin Akimov)

Pull request description:

  ## Issue being fixed or feature implemented
  Further improvements of `migratewallet` RPC to migrate legacy wallets to descriptor wallets.

  ## What was done?
  Backports:
   - bitcoin-core/gui#738
   - bitcoin#26638
   - bitcoin#26740

   + fix for old backport bitcoin#17261

  Further changes to be done in the next batch ; out-of-scope of current PR:
   - bitcoin-core/gui#824 (fix for dashpay#738), depends on bitcoin#27790
   - bitcoin#26021 (depends on dashpay#7037)
   - bitcoin#28067 (depends on 26021)
   - bitcoin#28038 (depends on 27217)
   - bitcoin#28125
   - bitcoin#28602
   - bitcoin#28609 [currently doesn't work as expected, probably depends on 28125]
   - bitcoin#28546
   - bitcoin#28868

  ## How Has This Been Tested?
  Run unit test & functional tests.
  Click in Qt-UI "Migrate wallet" - it works as expected.

  ## Breaking Changes
  None

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [ ] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

Top commit has no ACKs.

Tree-SHA512: 7f14fe3ac06f1fe62f99bc2775007f92c26886dcbd2cb6fcc9c122ca8a515f5b6b78768cc6626c26b55b7c29102bf896834df950f98b2aee0915c7e8a65b124d
… fee has been set

798430d wallet: Sanity check fee paid cannot be negative (Andrew Chow)
c1a84f1 wallet: Move fee underpayment check to after fee setting (Andrew Chow)
e5daf97 wallet: Rename nFeeRet in CreateTransactionInternal to current_fee (Andrew Chow)

Pull request description:

  Currently the fee underpayment check occurs right after we calculate what the transaction's fee should be. However the fee paid by the transaction at that time does not always match. Notably, when doing SFFO, the fee paid at that time will almost always be less than the fee required, which then required having a bypass of the underpayment check that results in SFFO payments going through when they should not.

  This PR moves the underpayment check to after fees have been finalized so that we always check whether the fee is being underpaid. This removes the exception for SFFO and unifies this behavior for both SFFO and non-SFFO txs.

ACKs for top commit:
  S3RK:
    Code review ACK 798430d
  furszy:
    Code review ACK 798430d
  glozow:
    utACK 798430d, code looks correct to me

Tree-SHA512: 720e8a3dbdc9937b12ee7881eb2ad58332c9584520da87ef3080e6f9d6220ce8d3bd8b9317b4877e56a229113437340852976db8f64df0d5cc50723fa04b02f0

Co-authored-by: Andrew Chow <github@achow101.com>
0452805 [bench] BlockAssembler with mempool packages (glozow)
6ce265a [test util] lock cs_main before pool.cs in PopulateMempool (glozow)
8791410 [test util] randomize fee in PopulateMempool (glozow)
cba5934 [miner] allow bypassing TestBlockValidity (glozow)
c058852 [refactor] parameterize BlockAssembler::Options in PrepareBlock (glozow)
a2de971 [refactor] add helper to apply ArgsManager to BlockAssembler::Options (glozow)

Pull request description:

  Performance of block template building matters as miners likely want to be able to start mining on a block with transactions asap after a block is found. We would want to know if a mempool PR accidentally caused, for example, a 100x slowdown. An `AssembleBlock()` bench exists, but it operates on a mempool with 101 transactions, each with 0 ancestors or descendants and with the same fee. Adding a bench with a more complex mempool is useful because (1) it's more realistic (2) updating packages can potentially cause the algorithm to take a long time.

ACKs for top commit:
  kevkevinpal:
    Tested ACK [0452805](bitcoin@0452805)
  achow101:
    ACK 0452805
  stickies-v:
    ACK 0452805

Tree-SHA512: 38c138d6a75616651f9b1faf4e3a1cd833437a486f4e84308fbee958e8462bb570582c88f7ba7ab99d80191e97855ac2cf27c43cc21585d3e4b0e227effe2fb5

Co-authored-by: Andrew Chow <github@achow101.com>
fae66fc test: Remove python3.5 workaround in authproxy (MarcoFalke)

Pull request description:

  Remove workaround for a bug that is long fixed in a EOL python version, that isn't used by us.

  If the workaround is still needed, it should at least log the exception before silently discarding it, so that debugging is possible/easier.

ACKs for top commit:
  fanquake:
    ACK fae66fc

Tree-SHA512: 9da28e495d530b9f9c5c75eff4982ef23b3775309e1f8d509722a9e7fd8b3535942c9a9cbd2d5e43e6487d46fdec4a63114aaa104e258c261cb98cb58560872a

Co-authored-by: fanquake <fanquake@gmail.com>
fabf1cd Use steady clock for bench logging (MacroFake)
faed342 scripted-diff: Rename time symbols (MacroFake)

Pull request description:

  Instead of using `0.001` and similar constants to "convert" an int64_t to milliseconds, use the type-safe `Ticks<>` helper. Also, use steady clock instead of system clock, since the durations are used for benchmarking.

ACKs for top commit:
  fanquake:
    ACK fabf1cd - validation bench output still looks sane.

Tree-SHA512: e6525b5fdad6045ca500c56014897d7428ad288aaf375933d3b5939feddf257f6910d562eb66ebcde9186bef9a604ee8d763a318253838318d59df2a285be7c2

Co-authored-by: MacroFake <falke.marco@gmail.com>
BACKPORT NOTE:
it has extra changes from partial bitcoin#26691

-----
f09d47b bench: Add missed `ECCVerifyHandle` instance (Hennadii Stepanov)

Pull request description:

  To clearly observe the lack of an `ECCVerifyHandle` instance,
  - apply the following diff:
  ```diff
  --- a/src/Makefile.bench.include
  +++ b/src/Makefile.bench.include
  @@ -19,11 +19,9 @@ bench_bench_bitcoin_SOURCES = \
     bench/bench.h \
     bench/bench_bitcoin.cpp \
     bench/block_assemble.cpp \
  -  bench/ccoins_caching.cpp \
     bench/chacha20.cpp \
     bench/chacha_poly_aead.cpp \
     bench/checkblock.cpp \
  -  bench/checkqueue.cpp \
     bench/crypto_hash.cpp \
     bench/data.cpp \
     bench/data.h \
  @@ -46,8 +44,7 @@ bench_bench_bitcoin_SOURCES = \
     bench/rpc_blockchain.cpp \
     bench/rpc_mempool.cpp \
     bench/strencodings.cpp \
  -  bench/util_time.cpp \
  -  bench/verify_script.cpp
  +  bench/util_time.cpp

   nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_FILES)

  ```
  - then
  ```
  $ ./autogen
  $ ./configure
  $ make clean
  $ make
  ```
  - then
  ```
  $ ./src/bench/bench_bitcoin -filter=ExpandDescriptor
  bench_bitcoin: pubkey.cpp:296: bool CPubKey::IsFullyValid() const: Assertion `secp256k1_context_verify && "secp256k1_context_verify must be initialized to use CPubKey."' failed.
  Aborted (core dumped)
  ```

ACKs for top commit:
  achow101:
    ACK f09d47b
  w0xlt:
    ACK bitcoin@f09d47b

Tree-SHA512: e1f33f88d427c57fe31d5810d12e9f46fed2911f5736208ebf7d4a968de0dd8c1f6b73a0d1093316da117dd3bcfda5dde6e41d6c95fcdb99bdea62e19df5ad20

Co-authored-by: MacroFake <falke.marco@gmail.com>
…m_next_resend

fa51cc9 refactor: Use type-safe time point for CWallet::m_next_resend (MacroFake)

Pull request description:

  `GetTime` is not type-safe, thus deprecated, see https://github.com/bitcoin/bitcoin/blob/75cbbfa279685f70d9f6fa71432df00862ffa865/src/util/time.h#L62-L70

ACKs for top commit:
  shaavan:
    Code Review ACK fa51cc9
  aureleoules:
    ACK fa51cc9

Tree-SHA512: 030de10070518580763ea75079442e2f934c54d3083be3ebe35e7f1bc6db2096745bb46d95aa1e6efe29ced30a048acfe5cd999178e6787b7647dfbec5ecb444

Co-authored-by: fanquake <fanquake@gmail.com>
…reatefundedpsbt

737c285 test: Don't pass add_to_wallet option to walletcreatefundedpsbt (Ryan Ofsky)

Pull request description:

  It's not a documented option. Noticed while working on bitcoin#19762

ACKs for top commit:
  achow101:
    ACK 737c285

Tree-SHA512: 1bf4186fae4390233b2f23389eb6c515c7f0209f12553592df5166e75c452ccd1fb125d9246047c08cff0b869fdda7793812d15da01441e2c4777514446f3ed6

Co-authored-by: Andrew Chow <github@achow101.com>
The second input of the extractor vector is a P2SH-P2WSH spend: its
UTXO and signatures are carried in witness_utxo and final_scriptwitness
fields, which Dash's PSBT implementation stores as unknown key-value
pairs, so the input can never be script-verified. The vector extracts
today only because PSBT finality is judged by final_script_sig being
non-empty, and it contradicts the finalizer vector, which was already
dashified to expect this input to stay unfinalized. Once finality is
checked by actual script execution (bitcoin#25595), extraction of this
vector must fail, so drop it. The extractor loop in rpc_psbt.py is kept
to preserve the upstream file structure.
… fields being empty

BACKPORT NOTE:
witness-scenario is omitted for backport; the new test provides the OP_TRUE prevout as non_witness_utxo, since Dash's PSBT has no witness_utxo field, and the expected hex is computed because the input spends a real prev-tx.
The segwit-based extractor vector was removed from rpc_psbt.json in a preceding commit, since its final scriptSig can no longer be script-verified.
-------------
e133264 Add test for PSBT input verification (Greg Sanders)
d256992 Verify PSBT inputs rather than check for fields being empty (Greg Sanders)

Pull request description:

  In a few keys spots, PSBT finality is checked by looking for non-empty witness data.

  This complicates a couple things:
  1) Empty data can be valid in certain cases
  2) User may be passed bogus final data by a counterparty during PSBT work happening, and end up with incorrect signatures that they may not be able to check in other contexts if the UTXO doesn't exist yet in chain/mempool, timelocks, etc.

  On the whole I think these heavier checks are worth it in case someone is actually assuming the signatures are correct if our API is saying so.

ACKs for top commit:
  achow101:
    ACK e133264

Tree-SHA512: 9de4fbb0be1257b081781f5df908fd55666e3acd5c4e36beb3b3f2f5a6aed69ff77068c44cde6127e159e773293fd9ced4c0bb47e693969f337e74dc8af030da

Co-authored-by: fanquake <fanquake@gmail.com>
…26695

6a5e88e miner: don't re-apply default Options value if argument is unset (stickies-v)
ea72c3d refactor: avoid duplicating BlockAssembler::Options members (stickies-v)
cba749a refactor: rename local gArgs to args (stickies-v)

Pull request description:

  Two follow-ups for bitcoin#26695, both refactoring and no observed (*) behaviour change:
  - Rename `gArgs` to `args` because it's not actually a global
  - Add `BlockAssembler::Options` as a (private) member to `BlockAssembler` to avoid having to assign all the options individually, essentially duplicating them

  Reduces LoC and makes the code more readable, in my opinion.

  ---

  (*) as [pointed out by ajtowns](bitcoin#26883 (comment)), this PR changes the interface of `ApplyArgsManOptions()`, making this not a pure refactoring PR. In practice, `ApplyArgsManOptions()` is never called in such a way that this leads to observed behaviour change. Regardless, I've carved out the potential behaviour change into a separate commit and would be okay with dropping it, should it turn out to be controversial.

ACKs for top commit:
  glozow:
    ACK 6a5e88e
  TheCharlatan:
    Light code review ACK 6a5e88e

Tree-SHA512: 15c30442ff0e070b1a58dc4c9615550d619ce35b4a2596b2c0a9d790259bbf987cab708f7cbb1057a8cf8b4c3226f3ad981282d3499ac442094806492a5f68ce

Co-authored-by: glozow <gloriajzhao@gmail.com>
…6302, bitcoin#26483, bitcoin#26643, bitcoin#26695, bitcoin#26883, bitcoin#27378, partial bitcoin#25595

bb61024 Merge bitcoin#26883: src/node/miner cleanups, follow-ups for bitcoin#26695 (Konstantin Akimov)
2aa0e7b partial Merge bitcoin#25595: Verify PSBT inputs rather than check for fields being empty (Konstantin Akimov)
1799196 test: remove segwit-based extractor vector from rpc_psbt.json (Konstantin Akimov)
56ba7c5 Merge bitcoin#26483: test: Don't pass add_to_wallet option to walletcreatefundedpsbt (Konstantin Akimov)
8239d69 Merge bitcoin#26302: refactor: Use type-safe time point for CWallet::m_next_resend (Konstantin Akimov)
78f24fa Merge bitcoin#26179: bench: Add missed `ECCVerifyHandle` instance (Konstantin Akimov)
f45ee6d Merge bitcoin#26118: log: Use steady clock for bench logging (Konstantin Akimov)
c1e9806 Merge bitcoin#27378: test: Remove python3.5 workaround (Konstantin Akimov)
cbb7aef Merge bitcoin#26695: bench: BlockAssembler on a mempool with packages (Konstantin Akimov)
c972731 Merge bitcoin#26643: wallet: Move fee underpayment check to after all fee has been set (Konstantin Akimov)

Pull request description:

  ## What was done?
  Regular backports from Bitcoin Core v25

  ## How Has This Been Tested?
  Dashified rpc_psbt.py changes
  No other special testing.

  ## Breaking Changes
  n/A

  ## Checklist:
  - [ ] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [ ] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

Top commit has no ACKs.

Tree-SHA512: e65393de84cb708c0aaca4cf3e1390ceeec8e714fb3210eea81bd896c73f0d7c220d46669f46c4792408c6686aeb240797a4c5fc5a4628d120d909aa0c31ee81
CDeterministicMNManager's in-memory caches (mnListsCache, mnListDiffsCache) are only trimmed by CleanupCache(), which runs when a new block arrives. Between blocks there is no bound: GETMNLISTDIFF accepts an arbitrary historical baseBlockHash and GetListForBlock appends a cache entry per requested block, so an unauthenticated peer requesting many distinct historical blocks drives cache growth without ceiling (a full mainnet MN list is several MB).

Bound admission through new CacheMNList()/CacheMNListDiff() helpers using two tiers. Tip-recent heights (height + LIST_DIFFS_CACHE_SIZE >= tip, the same window CleanupCache keeps) stay in mnListsCache with hard cap MAX_CACHE_LISTS = DISK_SNAPSHOT_PERIOD * 2, evicting lowest-height entries in a single pass (std::nth_element), never the tip snapshot. Lists are rebuilt by applying up to DISK_SNAPSHOT_PERIOD - 1 diffs from the previous on-disk snapshot, so validation/invalidation spanning a snapshot boundary can keep two snapshot periods of lists resident without eviction thrash. Stale heights route to a small LRU tier (MAX_STALE_CACHE_LISTS = 32) so repeated historical requests stay cheap after first warm-up instead of re-walking up to ~575 diffs under cs_main on every call. MAX_CACHE_DIFFS = LIST_DIFFS_CACHE_SIZE + 64.

The rebuild walk in GetListForBlockInternal() admits every diff it reads unconditionally so the apply loop can resolve every walked hash; the diff cap is enforced once after the walk completes, and again before the peer-reachable BlockDataUnavailableError throw so an aborted walk cannot leave the cache over its bound. UndoBlock() and WriteRepairedDiffs() invalidate the stale tier alongside the other two caches, so a stale entry built from pre-repair diffs can never shadow repaired disk data.

Add mn_lists_cache_bounded regression test: drive GetListForBlock over more distinct historical heights than the cap without running cleanup, assert both caches and the stale tier stay bounded, and prove eviction never changes a returned list by re-querying entries guaranteed to have been evicted.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants