-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat(wallet): external signer (hardware signer) #6019
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
This pull request has conflicts, please rebase. |
|
This pull request has conflicts, please rebase. |
|
This pull request has conflicts, please rebase. |
❌ Backport Verification - CATASTROPHIC FAILUREOriginal Bitcoin commit: Critical violations detected:
This PR violates fundamental backporting principles:
Recommendation: Create focused, smaller PRs that backport specific Bitcoin commits for hardware wallet functionality, following the established backporting process with:
This PR has been automatically closed due to catastrophic validation failures. |
|
Automatically closed due to catastrophic validation failures. Please see the detailed analysis above and create focused PRs with proper Bitcoin commit references and proportional scope. |
✅ No Merge Conflicts DetectedThis PR currently has no conflicts with other open PRs. |
|
This pull request has conflicts, please rebase. |
|
This pull request has conflicts, please rebase. |
WalkthroughReplaces build-time Boost.Process option with an opt-in ENABLE_EXTERNAL_SIGNER feature. Adds ExternalSigner API (header + implementation), Node/Wallet interface extensions, wallet integration (WALLET_FLAG_EXTERNAL_SIGNER, ExternalSignerScriptPubKeyMan, displayAddress/FillPSBT flows), RPCs (enumeratesigners, walletdisplayaddress), Qt UI wiring (options, create wallet, receive/send dialogs), tests and mocks, build/config updates (configure.ac, Makefiles, CI scripts), documentation, new error codes/DB enum for external-signer support, and exposes RunCommandParseJSON unconditionally while gating its implementation behind ENABLE_EXTERNAL_SIGNER. Sequence Diagram(s)sequenceDiagram
%% Styling: highlight external signer interaction
participant UI as Qt UI
participant Model as WalletModel
participant Wallet as CWallet
participant SPKM as ExternalSignerScriptPubKeyMan
participant Sys as RunCommandParseJSON
participant Ext as External Signer (script)
UI->>Model: user triggers send / verify
Model->>Wallet: create PSBT (sign=false) / display request
Wallet->>SPKM: FillPSBT / DisplayAddress
alt ENABLE_EXTERNAL_SIGNER && signer configured
SPKM->>Sys: RunCommandParseJSON("enumerate"/"displayaddress"/"signtx", stdin=PSBT/params)
Sys->>Ext: spawn process, write stdin
Ext-->>Sys: stdout JSON
Sys-->>SPKM: parsed UniValue or error
SPKM->>Wallet: signed PSBT / address confirmation or error
Wallet->>Model: finalized tx / result
Model->>UI: broadcast or show PSBT/save prompt or success
else signer unavailable or feature not compiled
SPKM-->>Wallet: return error / throw runtime_error
Wallet->>Model: propagate error
Model->>UI: show configuration/enable guidance
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Areas needing focused review:
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (17)
🚧 Files skipped from review as they are similar to previous changes (9)
🧰 Additional context used🧠 Learnings (15)📓 Common learnings📚 Learning: 2025-10-05T20:38:28.457ZApplied to files:
📚 Learning: 2025-11-04T18:24:27.241ZApplied to files:
📚 Learning: 2025-10-13T12:37:12.357ZApplied to files:
📚 Learning: 2025-10-25T07:08:51.918ZApplied to files:
📚 Learning: 2025-02-06T14:34:30.466ZApplied to files:
📚 Learning: 2025-01-14T09:07:12.446ZApplied to files:
📚 Learning: 2025-07-29T14:32:48.369ZApplied to files:
📚 Learning: 2025-08-19T14:57:31.801ZApplied to files:
📚 Learning: 2025-08-11T17:16:36.654ZApplied to files:
📚 Learning: 2025-08-01T07:46:37.840ZApplied to files:
📚 Learning: 2025-06-09T16:43:20.996ZApplied to files:
📚 Learning: 2025-07-23T09:30:34.631ZApplied to files:
📚 Learning: 2025-01-06T09:51:03.167ZApplied to files:
📚 Learning: 2025-01-02T08:33:26.751ZApplied to files:
🪛 LanguageTooldoc/external-signer.md[style] ~70-~70: Consider a more concise word here. (IN_ORDER_TO_PREMIUM) [grammar] ~167-~167: Use a hyphen to join words. (QB_NEW_EN_HYPHEN) 🪛 markdownlint-cli2 (0.18.1)doc/external-signer.md14-14: Dollar signs used before commands without showing output (MD014, commands-show-output) 25-25: Fenced code blocks should have a language specified (MD040, fenced-code-language) 40-40: Dollar signs used before commands without showing output (MD014, commands-show-output) 48-48: Dollar signs used before commands without showing output (MD014, commands-show-output) 49-49: Dollar signs used before commands without showing output (MD014, commands-show-output) 59-59: Dollar signs used before commands without showing output (MD014, commands-show-output) 80-80: Fenced code blocks should have a language specified (MD040, fenced-code-language) 98-98: Fenced code blocks should have a language specified (MD040, fenced-code-language) 115-115: Fenced code blocks should have a language specified (MD040, fenced-code-language) 122-122: Fenced code blocks should have a language specified (MD040, fenced-code-language) 141-141: Fenced code blocks should have a language specified (MD040, fenced-code-language) 147-147: Fenced code blocks should have a language specified (MD040, fenced-code-language) ⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
🔇 Additional comments (10)
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 26
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/qt/forms/createwalletdialog.ui (1)
150-156: Include Descriptor checkbox in tab orderDescriptor wallet option is missing from , which hurts keyboard navigation.
<tabstops> <tabstop>wallet_name_line_edit</tabstop> <tabstop>encrypt_wallet_checkbox</tabstop> <tabstop>disable_privkeys_checkbox</tabstop> <tabstop>blank_wallet_checkbox</tabstop> + <tabstop>descriptor_checkbox</tabstop> <tabstop>external_signer_checkbox</tabstop> </tabstops>src/test/system_tests.cpp (1)
39-43: Fix POSIX quoting in RunCommandParseJSON testThe JSON argument isn’t properly escaped; the shell will break on inner quotes.
- const UniValue result = RunCommandParseJSON("echo \"{\"success\": true}\""); + const UniValue result = RunCommandParseJSON("echo \"{\\\"success\\\": true}\"");
🧹 Nitpick comments (36)
test/functional/test_framework/test_node.py (1)
884-889: LGTM! Consider adding documentation for developer experience.The parameter addition is implemented correctly—the signature and call order match the RPC definition in
src/wallet/rpc/wallet.cpp. The default value ofNoneappropriately maps to the RPC's default offalse.However, the method signature has grown to 8 parameters without documentation. Consider adding a docstring that explains:
- The purpose and type of each parameter
- That
external_signer=Truerequires bothdisable_private_keys=Trueanddescriptors=True(per the RPC implementation)This would improve the developer experience when writing tests that use external signers.
Example docstring addition:
+ def createwallet(self, wallet_name, disable_private_keys=None, blank=None, passphrase='', avoid_reuse=None, descriptors=None, load_on_startup=None, external_signer=None): + """Create a new wallet via the createwallet RPC. + + Args: + wallet_name: Name for the new wallet + disable_private_keys: Disable private keys (watchonly mode) + blank: Create a blank wallet (no keys or HD seed) + passphrase: Encryption passphrase + avoid_reuse: Enable coin reuse avoidance + descriptors: Create a descriptor wallet + load_on_startup: Add wallet to startup list + external_signer: Use external signer (requires disable_private_keys=True and descriptors=True) + """ if descriptors is None:Based on learnings: Good documentation is especially important in test framework code where multiple developers write tests.
test/functional/rpc_help.py (1)
114-115: Make the titles comparison fully order‑independentTo avoid brittle failures if RPC help ordering changes, sort both sides.
- assert_equal(titles, sorted(components)) + assert_equal(sorted(titles), sorted(components))src/qt/optionsmodel.cpp (1)
226-232: Plumbing looks right; consider hiding when not supportedPersisting external_signer_path and SoftSetArg("-signer") is fine. To avoid confusing users, consider gating this option in the UI when ENABLE_EXTERNAL_SIGNER is off (e.g., hide row or mark read-only).
test/functional/mocks/signer.py (2)
11-19: Handle empty mock_result safelyAvoid index errors when mock_result exists but is empty.
def perform_pre_checks(): mock_result_path = os.path.join(os.getcwd(), "mock_result") if(os.path.isfile(mock_result_path)): with open(mock_result_path, "r", encoding="utf8") as f: - mock_result = f.read() - if mock_result[0]: + mock_result = f.read() + if mock_result: sys.stdout.write(mock_result[2:]) sys.exit(int(mock_result[0]))
20-22: Normalize indentation to 4 spacesUse 4-space indents to satisfy linters and maintain consistency.
-def enumerate(args): - sys.stdout.write(json.dumps([{"fingerprint": "00000001", "type": "trezor", "model": "trezor_t"}, {"fingerprint": "00000002"}])) +def enumerate(args): + sys.stdout.write(json.dumps([{"fingerprint": "00000001", "type": "trezor", "model": "trezor_t"}, {"fingerprint": "00000002"}])) @@ - sys.argv.extend(buffer.rstrip().split(" ")) + sys.argv.extend(buffer.rstrip().split(" "))Also applies to: 96-97
src/qt/sendcoinsdialog.cpp (1)
540-545: Avoid tfm::format to stderr hereThis may not compile without including tinyformat and won’t surface in the GUI. Prefer Q_EMIT message or qWarning().
- if (err != TransactionError::OK) { - tfm::format(std::cerr, "Failed to sign PSBT"); - processSendCoinsReturn(WalletModel::TransactionCreationFailed); + if (err != TransactionError::OK) { + processSendCoinsReturn(WalletModel::TransactionCreationFailed);src/qt/walletmodel.h (1)
144-144: Pass string parameter by const reference.The
sAddressparameter creates an unnecessary copy. For strings, pass byconst std::string&to avoid allocation overhead.Apply this diff:
- bool displayAddress(std::string sAddress); + bool displayAddress(const std::string& sAddress);Note: The corresponding implementation in src/qt/walletmodel.cpp (line 581) should also be updated.
src/qt/forms/optionsdialog.ui (1)
412-441: Add a “Browse…” control and minor UX polish for the signer path.
- Add a browse button to reduce path-typing errors.
- Enable the clear button and a placeholder for discoverability.
- Ensure the group is hidden/disabled at runtime when ENABLE_EXTERNAL_SIGNER is off (guard in optionsdialog.cpp).
Apply minimal UI tweaks:
<layout class="QVBoxLayout" name="verticalLayoutHww"> <item> <layout class="QHBoxLayout" name="horizontalLayoutHww"> <item> <widget class="QLabel" name="externalSignerPathLabel"> @@ </item> <item> <widget class="QLineEdit" name="externalSignerPath"> <property name="toolTip"> <string>Full path to a Dash Core compatible script (e.g. C:\Downloads\hwi.exe or /Users/you/Downloads/hwi.py). Beware: malware can steal your coins!</string> </property> + <property name="placeholderText"> + <string>/path/to/hwi(.py|.exe)</string> + </property> + <property name="clearButtonEnabled"> + <bool>true</bool> + </property> </widget> </item> + <item> + <widget class="QToolButton" name="externalSignerBrowse"> + <property name="text"> + <string>…</string> + </property> + <property name="toolTip"> + <string>Browse for signer executable/script</string> + </property> + </widget> + </item> </layout> </item> </layout>Please confirm optionsdialog.cpp hides/disables groupBoxHww when external signer is not compiled. If not, I can draft the guard.
src/interfaces/node.h (1)
160-168: Const-correctness and naming clarity for interfaces::ExternalSigner.
- Make getName() const to allow querying name on const instances and align with typical interface contracts.
- Since there is a global ::ExternalSigner, consider explicitly documenting the distinction or renaming this interface (e.g., IExternalSigner) to avoid confusion in mixed scopes.
-class ExternalSigner +class ExternalSigner { public: virtual ~ExternalSigner() {}; - //! Get signer display name - virtual std::string getName() = 0; + //! Get signer display name + virtual std::string getName() const = 0; };src/qt/walletmodel.cpp (1)
582-592: Consider passing a parent widget to QMessageBox.The method correctly handles exceptions from the wallet layer and displays them to the user. However,
QMessageBox::critical(nullptr, ...)uses a null parent, which may result in the dialog appearing disconnected from the main window.Consider accepting an optional parent widget parameter to improve the UX.
Apply this diff if you want to improve dialog parenting:
-bool WalletModel::displayAddress(std::string sAddress) +bool WalletModel::displayAddress(std::string sAddress, QWidget* parent) { CTxDestination dest = DecodeDestination(sAddress); bool res = false; try { res = m_wallet->displayAddress(dest); } catch (const std::runtime_error& e) { - QMessageBox::critical(nullptr, tr("Can't display address"), e.what()); + QMessageBox::critical(parent, tr("Can't display address"), e.what()); } return res; }src/util/system.cpp (1)
1399-1434: Harden external command execution: catch spawn errors and read full streamsTwo improvements:
- Propagate spawn failures uniformly (e.g., missing binary) by catching boost::process errors.
- Read the entire stdout/stderr to avoid truncation on multi-line JSON and reduce deadlock risk if output exceeds a line.
Apply this diff:
#ifdef ENABLE_EXTERNAL_SIGNER namespace bp = boost::process; UniValue result_json; bp::opstream stdin_stream; bp::ipstream stdout_stream; bp::ipstream stderr_stream; if (str_command.empty()) return UniValue::VNULL; - bp::child c( - str_command, - bp::std_out > stdout_stream, - bp::std_err > stderr_stream, - bp::std_in < stdin_stream - ); - if (!str_std_in.empty()) { - stdin_stream << str_std_in << std::endl; - } - stdin_stream.pipe().close(); - - std::string result; - std::string error; - std::getline(stdout_stream, result); - std::getline(stderr_stream, error); - - c.wait(); - const int n_error = c.exit_code(); - if (n_error) throw std::runtime_error(strprintf("RunCommandParseJSON error: process(%s) returned %d: %s\n", str_command, n_error, error)); - if (!result_json.read(result)) throw std::runtime_error("Unable to parse JSON: " + result); + try { + bp::child c( + str_command, + bp::std_out > stdout_stream, + bp::std_err > stderr_stream, + bp::std_in < stdin_stream + ); + if (!str_std_in.empty()) { + stdin_stream << str_std_in << std::endl; + } + stdin_stream.pipe().close(); + + std::string result{std::istreambuf_iterator<char>(stdout_stream), std::istreambuf_iterator<char>()}; + std::string error{std::istreambuf_iterator<char>(stderr_stream), std::istreambuf_iterator<char>()}; + + c.wait(); + const int n_error = c.exit_code(); + if (n_error) throw std::runtime_error(strprintf("RunCommandParseJSON error: process(%s) returned %d: %s\n", str_command, n_error, error)); + if (!result_json.read(result)) throw std::runtime_error("Unable to parse JSON: " + result); + } catch (const std::exception& e) { + throw std::runtime_error(strprintf("RunCommandParseJSON spawn/IO failure for '%s': %s", str_command, e.what())); + }doc/external-signer.md (3)
13-15: Add fenced code languages and address markdownlint MD014/MD040Specify languages (bash/json) and either drop leading “$” or include sample outputs to satisfy MD014. Improves readability and CI lint.
Also applies to: 25-33, 39-41, 47-50, 59-61, 80-87, 98-101, 115-119, 122-136, 141-149
70-70: Tighten phrasingReplace “In order to be compatible…” with “To be compatible…”.
167-167: Hyphenation nitUse “BIP44/49/84-compatible” for correct hyphenation.
src/qt/optionsdialog.cpp (1)
141-145: UI polish when feature is unavailableBesides disabling the field, consider hiding the entire row (label + editor) to reduce clutter when external signer support is not compiled.
src/qt/walletcontroller.cpp (1)
296-303: List-and-pass signers flow is OK; handle exceptionsCatching std::runtime_error and surfacing to users is good. Consider also logging the exception in the node logger for diagnostics.
test/functional/rpc_signer.py (2)
52-54: Fix flake8 E128 indentation on continued linesAdjust hanging indent for assert_raises_rpc_error calls.
- assert_raises_rpc_error(-1, 'Error: restart dashd with -signer=<cmd>', - self.nodes[0].enumeratesigners - ) + assert_raises_rpc_error( + -1, 'Error: restart dashd with -signer=<cmd>', + self.nodes[0].enumeratesigners, + ) @@ - assert_raises_rpc_error(-1, 'execve failed: No such file or directory', - self.nodes[3].enumeratesigners - ) + assert_raises_rpc_error( + -1, 'execve failed: No such file or directory', + self.nodes[3].enumeratesigners, + ) @@ - assert_raises_rpc_error(-1, 'RunCommandParseJSON error', - self.nodes[1].enumeratesigners - ) + assert_raises_rpc_error( + -1, 'RunCommandParseJSON error', + self.nodes[1].enumeratesigners, + ) @@ - assert_raises_rpc_error(-1, 'fingerprint not found', - self.nodes[1].enumeratesigners - ) + assert_raises_rpc_error( + -1, 'fingerprint not found', + self.nodes[1].enumeratesigners, + )Also applies to: 57-58, 63-65, 69-70
45-47: Make cleanup resilientIgnore missing mock_result file during cleanup to avoid spurious failures.
- def clear_mock_result(self, node): - os.remove(os.path.join(node.cwd, "mock_result")) + def clear_mock_result(self, node): + try: + os.remove(os.path.join(node.cwd, "mock_result")) + except FileNotFoundError: + passtest/functional/mocks/invalid_signer.py (2)
23-39: Descriptor samples: fix duplicated variable and address type for 84'Rename the second xpub_pkh to xpub_wpkh and use wpkh() for 84'/… paths.
- xpub_pkh = "xpub6CRhJvXV8x2AKWvqi1ZSMFU6cbxzQiYrv3dxSUXCawjMJ1JzpqVsveH4way1yCmJm29KzH1zrVZmVwes4Qo6oXVE1HFn4fdiKrYJngqFFc6" - xpub_sh = "xpub6CoNoq3Tg4tGSpom2BSwL42gy864KHo3TXkHxLxBbhvCkgmdVXADQmiHbLZhX3Me1cYhRx7s25Lpm4LnT5zu395ANHsXB2QvT9tqJDAibTN" - xpub_pkh = "xpub6DUcLgY1DfgDy2RV6q4djwwsLitaoZDumbribqrR8mP78fEtgZa1XEsqT5MWQ7gwLwKsTQPT28XLoVE5A97rDNTwMXjmzPaNijoCApCbWvp" + xpub_pkh = "xpub6CRhJvXV8x2AKWvqi1ZSMFU6cbxzQiYrv3dxSUXCawjMJ1JzpqVsveH4way1yCmJm29KzH1zrVZmVwes4Qo6oXVE1HFn4fdiKrYJngqFFc6" + xpub_sh = "xpub6CoNoq3Tg4tGSpom2BSwL42gy864KHo3TXkHxLxBbhvCkgmdVXADQmiHbLZhX3Me1cYhRx7s25Lpm4LnT5zu395ANHsXB2QvT9tqJDAibTN" + xpub_wpkh = "xpub6DUcLgY1DfgDy2RV6q4djwwsLitaoZDumbribqrR8mP78fEtgZa1XEsqT5MWQ7gwLwKsTQPT28XLoVE5A97rDNTwMXjmzPaNijoCApCbWvp" @@ - "pkh([b3c19bfc/84'/1'/" + args.account + "']" + xpub_pkh + "/0/*)#jftn8ppv" + "wpkh([b3c19bfc/84'/1'/" + args.account + "']" + xpub_wpkh + "/0/*)#jftn8ppv" @@ - "pkh([b3c19bfc/84'/1'/" + args.account + "']" + xpub_pkh + "/1/*)#rawj6535" + "wpkh([b3c19bfc/84'/1'/" + args.account + "']" + xpub_wpkh + "/1/*)#rawj6535"
20-22: Unused function argument is acceptable hereRuff’s ARG001 for enumerate(args) can be ignored since argparse mandates the signature; or prefix with
_args.src/wallet/external_signer_scriptpubkeyman.cpp (2)
42-50: Selecting the first signer is fragileWhen multiple devices are present, defaulting to index 0 is ambiguous. Accept a fingerprint preference (CLI/UI) and select by match; otherwise surface a clear error.
- if (signers.empty()) throw std::runtime_error(std::string(__func__) + ": No external signers found"); - // TODO: add fingerprint argument in case of multiple signers - return signers[0]; + if (signers.empty()) throw std::runtime_error(std::string(__func__) + ": No external signers found"); + // TODO: wire a preferred fingerprint from args/UI and select by it; for now pick the first as a fallback. + return signers[0];
78-85: OK to rely on prior PSBT fill; consider documentingRelying on a prior non-signing FillPSBT pass is fine; consider a comment that inputs must carry bip32 derivations before invoking the signer.
src/qt/createwalletdialog.cpp (3)
9-9: Remove unused include to speed up builds.
#include <interfaces/node.h>isn’t used in this TU; header already declaressetSigners. Please drop it.-#include <interfaces/node.h>
42-45: Tiny typo in comment.“immedidately” → “immediately”.
-// watch-only wallet, even though we immedidately fetch keys from the +// watch-only wallet, even though we immediately fetch keys from the
115-134: setSigners(): robust defaults; consider empty label guard.Prefilling the wallet name from the first signer is nice. Add a fallback if
getName()is empty to keep the OK button logic predictable.-const std::string label = signers[0]->getName(); -ui->wallet_name_line_edit->setText(QString::fromStdString(label)); +const std::string label = signers[0]->getName(); +ui->wallet_name_line_edit->setText(label.empty() ? tr("Hardware wallet") : QString::fromStdString(label));src/wallet/wallet.cpp (2)
2505-2518: DisplayAddress ignores device result; return actual outcome.Currently always returns true despite TODO. Consider checking the signer response and surfacing failure.
-ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner(); -return signer_spk_man->DisplayAddress(scriptPubKey, signer); +ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner(); +return signer_spk_man->DisplayAddress(scriptPubKey, signer);And inside
ExternalSignerScriptPubKeyMan::DisplayAddress(...), parse theUniValueand return false on error (see header/cpp comment below).
3818-3851: Descriptor setup paths look correct; two small improvements.
- Non‑signer branch: OK.
- Signer branch: consider validating
GetDescriptors()keys exist and logging unexpected shapes; also avoidcontinuesilently on!GetOutputType().- if (!descriptor_vals.isArray()) throw std::runtime_error(...); + if (!descriptor_vals.isArray()) { + throw std::runtime_error(std::string(__func__) + ": Unexpected result (no descriptor array)"); + } @@ - if (!desc->GetOutputType()) { - continue; - } + if (!desc->GetOutputType()) { + // Skip non-spendable descriptors but log once for visibility. + m_storage.WalletLogPrintf("Skipping descriptor without recognized output type: %s\n", desc_str); + continue; + }Also applies to: 3852-3881
src/wallet/external_signer_scriptpubkeyman.h (1)
30-33: API contract: return real device outcome.Please make
DisplayAddressreturn the parsed success/failure from the signer command instead of unconditionallytrue(see wallet.cpp comment).src/node/interfaces.cpp (1)
16-16: Include external_signer.h unconditionallyOptional: guard the include to reduce compile surface when feature is disabled and to keep headers tighter.
-#include <external_signer.h> +#ifdef ENABLE_EXTERNAL_SIGNER +#include <external_signer.h> +#endifsrc/wallet/wallet.h (2)
30-31: Avoid pulling external_signer.h into wallet.hHeader coupling. wallet.h doesn't need ExternalSigner types in its public interface. Include externally in wallet.cpp instead, or guard by ENABLE_EXTERNAL_SIGNER to keep builds without the feature leaner.
-#include <external_signer.h> +#ifdef ENABLE_EXTERNAL_SIGNER +#include <external_signer.h> +#endif
697-704: Preserve [[nodiscard]] and align n_signed typesConsider keeping [[nodiscard]] on FillPSBT to prevent silent error ignoring. Also, n_signed here is size_t*, but ScriptPubKeyMan::FillPSBT uses int*. Align types across APIs to avoid accidental narrowing.
-TransactionError FillPSBT(PartiallySignedTransaction& psbtx, +[[nodiscard]] TransactionError FillPSBT(PartiallySignedTransaction& psbtx, @@ - size_t* n_signed = nullptr, + int* n_signed = nullptr,If changing the type is disruptive now, at least document the mixed int/size_t and cast explicitly in the implementation.
src/external_signer.h (2)
24-35: Pass-by-reference and namingUse const std::string& for chain to avoid copying; update comment to say “Dash” rather than “Bitcoin”.
- ExternalSigner(const std::string& command, const std::string chain, const std::string& fingerprint, const std::string name); + ExternalSigner(const std::string& command, const std::string& chain, const std::string& fingerprint, const std::string& name); @@ - //! Bitcoin mainnet, testnet, etc + //! Network (mainnet, testnet, etc.)
49-53: DisplayAddress return type vs actual usageCallers ignore the UniValue and just rely on exceptions. Either parse and validate here and return bool, or document that this throws on errors and the value is unused. Prefer returning bool and inspecting result to make walletdisplayaddress more predictable.
test/functional/wallet_signer.py (2)
21-34: Use sys.executable and quote paths for -signerAvoid “py ” launcher assumptions and spaces-in-path issues. Use the current interpreter path and quote.
+import sys @@ - if platform.system() == "Windows": - return "py " + path - else: - return path + return f"\"{sys.executable}\" \"{path}\"" @@ - if platform.system() == "Windows": - return "py " + path - else: - return path + return f"\"{sys.executable}\" \"{path}\""
48-53: Robust mock_result cleanupGuard against missing file to avoid spurious test errors.
def clear_mock_result(self, node): - os.remove(os.path.join(node.cwd, "mock_result")) + p = os.path.join(node.cwd, "mock_result") + if os.path.exists(p): + os.remove(p)src/external_signer.cpp (1)
31-31: Avoid copying UniValue objects in the loop.The loop creates a copy of each
UniValueobject. Use a const reference for better performance.Apply this diff:
- for (UniValue signer : result.getValues()) { + for (const UniValue& signer : result.getValues()) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📥 Commits
Reviewing files that changed from the base of the PR and between 764893d and e4004df4b14186f0f46ff69c95ea74a2fbf85d81.
📒 Files selected for processing (68)
ci/test/00_setup_env_arm.sh(1 hunks)ci/test/00_setup_env_mac.sh(1 hunks)ci/test/00_setup_env_mac_native_x86_64.sh(1 hunks)ci/test/00_setup_env_native_asan.sh(1 hunks)ci/test/00_setup_env_native_fuzz.sh(1 hunks)ci/test/00_setup_env_native_multiprocess.sh(1 hunks)ci/test/00_setup_env_native_nowallet.sh(1 hunks)ci/test/00_setup_env_native_qt5.sh(1 hunks)ci/test/00_setup_env_native_tsan.sh(1 hunks)ci/test/00_setup_env_s390x.sh(1 hunks)ci/test/00_setup_env_win64.sh(1 hunks)configure.ac(7 hunks)doc/Doxyfile.in(1 hunks)doc/external-signer.md(1 hunks)src/Makefile.am(5 hunks)src/Makefile.test.include(1 hunks)src/dummywallet.cpp(1 hunks)src/external_signer.cpp(1 hunks)src/external_signer.h(1 hunks)src/interfaces/node.h(2 hunks)src/interfaces/wallet.h(2 hunks)src/node/interfaces.cpp(3 hunks)src/qt/createwalletdialog.cpp(4 hunks)src/qt/createwalletdialog.h(2 hunks)src/qt/forms/createwalletdialog.ui(2 hunks)src/qt/forms/optionsdialog.ui(1 hunks)src/qt/forms/receiverequestdialog.ui(1 hunks)src/qt/optionsdialog.cpp(3 hunks)src/qt/optionsmodel.cpp(3 hunks)src/qt/optionsmodel.h(1 hunks)src/qt/receiverequestdialog.cpp(1 hunks)src/qt/sendcoinsdialog.cpp(7 hunks)src/qt/walletcontroller.cpp(4 hunks)src/qt/walletmodel.cpp(2 hunks)src/qt/walletmodel.h(1 hunks)src/qt/walletmodeltransaction.cpp(1 hunks)src/qt/walletmodeltransaction.h(1 hunks)src/rpc/client.cpp(1 hunks)src/rpc/external_signer.cpp(1 hunks)src/rpc/register.h(2 hunks)src/test/system_tests.cpp(2 hunks)src/util/error.cpp(1 hunks)src/util/error.h(1 hunks)src/util/system.cpp(3 hunks)src/util/system.h(0 hunks)src/wallet/external_signer_scriptpubkeyman.cpp(1 hunks)src/wallet/external_signer_scriptpubkeyman.h(1 hunks)src/wallet/init.cpp(1 hunks)src/wallet/interfaces.cpp(2 hunks)src/wallet/rpc/addresses.cpp(1 hunks)src/wallet/rpc/backup.cpp(1 hunks)src/wallet/rpc/spend.cpp(1 hunks)src/wallet/rpc/wallet.cpp(6 hunks)src/wallet/scriptpubkeyman.h(2 hunks)src/wallet/wallet.cpp(17 hunks)src/wallet/wallet.h(5 hunks)src/wallet/walletdb.cpp(1 hunks)src/wallet/walletdb.h(1 hunks)src/wallet/walletutil.h(1 hunks)test/config.ini.in(1 hunks)test/functional/mocks/invalid_signer.py(1 hunks)test/functional/mocks/signer.py(1 hunks)test/functional/rpc_help.py(1 hunks)test/functional/rpc_signer.py(1 hunks)test/functional/test_framework/test_framework.py(1 hunks)test/functional/test_framework/test_node.py(1 hunks)test/functional/test_runner.py(1 hunks)test/functional/wallet_signer.py(1 hunks)
💤 Files with no reviewable changes (1)
- src/util/system.h
🧰 Additional context used
📓 Path-based instructions (5)
src/**/*.{cpp,h,cc,cxx,hpp}
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.{cpp,h,cc,cxx,hpp}: Dash Core C++ codebase must be written in C++20 and require at least Clang 16 or GCC 11.1
Dash uses unordered_lru_cache for efficient caching with LRU eviction
Files:
src/util/error.hsrc/qt/walletmodeltransaction.hsrc/qt/walletmodel.hsrc/rpc/register.hsrc/qt/receiverequestdialog.cppsrc/wallet/rpc/addresses.cppsrc/wallet/init.cppsrc/wallet/rpc/backup.cppsrc/wallet/walletdb.cppsrc/util/system.cppsrc/wallet/walletdb.hsrc/qt/walletmodeltransaction.cppsrc/wallet/scriptpubkeyman.hsrc/interfaces/node.hsrc/qt/optionsdialog.cppsrc/test/system_tests.cppsrc/util/error.cppsrc/wallet/external_signer_scriptpubkeyman.cppsrc/dummywallet.cppsrc/interfaces/wallet.hsrc/qt/createwalletdialog.cppsrc/rpc/external_signer.cppsrc/node/interfaces.cppsrc/qt/optionsmodel.hsrc/rpc/client.cppsrc/qt/walletcontroller.cppsrc/wallet/interfaces.cppsrc/wallet/walletutil.hsrc/qt/walletmodel.cppsrc/wallet/external_signer_scriptpubkeyman.hsrc/external_signer.cppsrc/qt/optionsmodel.cppsrc/qt/sendcoinsdialog.cppsrc/wallet/rpc/wallet.cppsrc/wallet/wallet.hsrc/wallet/wallet.cppsrc/external_signer.hsrc/qt/createwalletdialog.hsrc/wallet/rpc/spend.cpp
ci/**
📄 CodeRabbit inference engine (CLAUDE.md)
Unless specifically prompted, avoid making changes to the ci directory (continuous integration)
Files:
ci/test/00_setup_env_native_nowallet.shci/test/00_setup_env_s390x.shci/test/00_setup_env_mac_native_x86_64.shci/test/00_setup_env_mac.shci/test/00_setup_env_native_qt5.shci/test/00_setup_env_native_tsan.shci/test/00_setup_env_native_fuzz.shci/test/00_setup_env_arm.shci/test/00_setup_env_native_multiprocess.shci/test/00_setup_env_win64.shci/test/00_setup_env_native_asan.sh
test/functional/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Functional tests should be written in Python and placed in test/functional/
Files:
test/functional/test_framework/test_framework.pytest/functional/test_runner.pytest/functional/wallet_signer.pytest/functional/rpc_help.pytest/functional/test_framework/test_node.pytest/functional/rpc_signer.pytest/functional/mocks/signer.pytest/functional/mocks/invalid_signer.py
src/{test,wallet/test,qt/test}/**/*.{cpp,h,cc,cxx,hpp}
📄 CodeRabbit inference engine (CLAUDE.md)
Unit tests for C++ code should be placed in src/test/, src/wallet/test/, or src/qt/test/ and use Boost::Test or Qt 5 for GUI tests
Files:
src/test/system_tests.cpp
doc/**
📄 CodeRabbit inference engine (CLAUDE.md)
Unless specifically prompted, avoid making changes to the doc directory (documentation)
Files:
doc/Doxyfile.indoc/external-signer.md
🧬 Code graph analysis (28)
src/qt/walletmodeltransaction.h (1)
src/qt/walletmodeltransaction.cpp (2)
setWtx(29-32)setWtx(29-29)
src/qt/walletmodel.h (1)
src/qt/walletmodel.cpp (2)
displayAddress(582-592)displayAddress(582-582)
src/rpc/register.h (2)
src/rpc/external_signer.cpp (2)
RegisterSignerRPCCommands(61-73)RegisterSignerRPCCommands(61-61)src/node/interfaces.cpp (1)
ENABLE_EXTERNAL_SIGNER(530-551)
src/wallet/rpc/addresses.cpp (2)
src/wallet/rpc/util.cpp (2)
GetWalletForJSONRPCRequest(55-77)GetWalletForJSONRPCRequest(55-55)src/wallet/interfaces.cpp (18)
LOCK(250-259)LOCK(260-263)LOCK(291-295)LOCK(296-300)LOCK(353-361)LOCK(479-491)dest(210-214)dest(210-210)dest(223-226)dest(223-223)dest(227-230)dest(227-227)dest(231-249)dest(231-234)dest(264-268)dest(264-264)dest(269-273)dest(269-269)
src/wallet/init.cpp (2)
src/node/interfaces.cpp (1)
ENABLE_EXTERNAL_SIGNER(530-551)src/dummywallet.cpp (1)
argsman(31-31)
src/wallet/walletdb.cpp (1)
src/node/interfaces.cpp (1)
ENABLE_EXTERNAL_SIGNER(530-551)
src/util/system.cpp (1)
src/node/interfaces.cpp (1)
ENABLE_EXTERNAL_SIGNER(530-551)
src/wallet/scriptpubkeyman.h (2)
src/wallet/walletutil.h (1)
WalletDescriptor(70-103)src/wallet/external_signer_scriptpubkeyman.cpp (2)
SetupDescriptor(17-40)SetupDescriptor(17-17)
src/interfaces/node.h (2)
src/external_signer.cpp (1)
ExternalSigner(17-17)src/external_signer.h (1)
ExternalSigner(18-64)
src/qt/optionsdialog.cpp (1)
src/node/interfaces.cpp (1)
ENABLE_EXTERNAL_SIGNER(530-551)
src/test/system_tests.cpp (1)
src/node/interfaces.cpp (1)
ENABLE_EXTERNAL_SIGNER(530-551)
src/util/error.cpp (1)
src/util/translation.h (1)
Untranslated(48-48)
src/wallet/external_signer_scriptpubkeyman.cpp (3)
src/wallet/wallet.cpp (24)
batch(550-550)batch(815-815)batch(938-938)batch(1178-1178)batch(1257-1257)batch(1313-1313)batch(1358-1358)batch(1370-1370)batch(1391-1391)batch(1515-1515)batch(1660-1660)batch(1744-1744)batch(1793-1793)batch(2239-2239)batch(2300-2300)batch(2307-2307)DisplayAddress(2505-2518)DisplayAddress(2505-2505)GetSolvingProvider(3734-3738)GetSolvingProvider(3734-3734)GetSolvingProvider(3740-3748)GetSolvingProvider(3740-3740)FillPSBT(2032-2081)FillPSBT(2032-2032)src/wallet/scriptpubkeyman.cpp (16)
GetID(801-804)GetID(801-801)GetID(2375-2379)GetID(2375-2375)TopUp(1410-1413)TopUp(1410-1410)TopUp(1943-2004)TopUp(1943-1943)GetSolvingProvider(678-681)GetSolvingProvider(678-678)GetSolvingProvider(2231-2234)GetSolvingProvider(2231-2231)FillPSBT(734-778)FillPSBT(734-734)FillPSBT(2288-2355)FillPSBT(2288-2288)src/external_signer.cpp (4)
Enumerate(24-60)Enumerate(24-24)DisplayAddress(62-65)DisplayAddress(62-62)
test/functional/rpc_help.py (2)
test/functional/test_framework/test_framework.py (2)
is_external_signer_compiled(1115-1117)is_zmq_compiled(1135-1137)test/functional/test_framework/util.py (1)
assert_equal(69-74)
src/interfaces/wallet.h (2)
src/qt/walletmodel.cpp (2)
displayAddress(582-592)displayAddress(582-582)src/wallet/interfaces.cpp (12)
dest(210-214)dest(210-210)dest(223-226)dest(223-223)dest(227-230)dest(227-227)dest(231-249)dest(231-234)dest(264-268)dest(264-264)dest(269-273)dest(269-269)
test/functional/test_framework/test_node.py (1)
src/wallet/rpc/wallet.cpp (2)
createwallet(615-713)createwallet(615-615)
src/qt/createwalletdialog.cpp (2)
src/node/interfaces.cpp (1)
ENABLE_EXTERNAL_SIGNER(530-551)src/qt/createwalletdialog.h (1)
CreateWalletDialog(24-44)
test/functional/rpc_signer.py (3)
test/functional/test_framework/test_framework.py (1)
skip_if_no_external_signer(1106-1109)test/functional/test_framework/util.py (2)
assert_equal(69-74)assert_raises_rpc_error(132-148)test/functional/wallet_signer.py (6)
mock_signer_path(21-26)set_test_params(35-41)skip_test_if_missing_module(43-45)set_mock_result(47-49)clear_mock_result(51-52)run_test(54-57)
src/rpc/external_signer.cpp (2)
src/node/interfaces.cpp (1)
ENABLE_EXTERNAL_SIGNER(530-551)src/external_signer.cpp (2)
Enumerate(24-60)Enumerate(24-24)
src/node/interfaces.cpp (1)
src/external_signer.cpp (2)
Enumerate(24-60)Enumerate(24-24)
src/qt/walletmodel.cpp (1)
src/wallet/interfaces.cpp (28)
dest(210-214)dest(210-210)dest(223-226)dest(223-223)dest(227-230)dest(227-227)dest(231-249)dest(231-234)dest(264-268)dest(264-264)dest(269-273)dest(269-269)m_wallet(158-161)m_wallet(166-166)m_wallet(175-175)m_wallet(176-176)m_wallet(182-182)m_wallet(183-183)m_wallet(443-446)m_wallet(447-450)m_wallet(522-522)m_wallet(523-523)m_wallet(524-524)m_wallet(525-525)m_wallet(526-526)m_wallet(527-527)m_wallet(532-532)m_wallet(592-592)
src/wallet/external_signer_scriptpubkeyman.h (5)
src/wallet/scriptpubkeyman.h (6)
wallet(28-241)DescriptorScriptPubKeyMan(512-634)WalletStorage(34-234)bool(179-179)bool(218-218)bool(224-224)src/wallet/walletutil.h (1)
WalletDescriptor(70-103)src/wallet/external_signer_scriptpubkeyman.cpp (8)
SetupDescriptor(17-40)SetupDescriptor(17-17)GetExternalSigner(42-50)GetExternalSigner(42-42)DisplayAddress(52-61)DisplayAddress(52-52)FillPSBT(64-85)FillPSBT(64-64)src/external_signer.cpp (2)
DisplayAddress(62-65)DisplayAddress(62-62)src/wallet/wallet.cpp (4)
DisplayAddress(2505-2518)DisplayAddress(2505-2505)FillPSBT(2032-2081)FillPSBT(2032-2032)
src/external_signer.cpp (2)
src/external_signer.h (1)
ExternalSigner(18-64)src/util/system.cpp (2)
RunCommandParseJSON(1397-1434)RunCommandParseJSON(1397-1397)
src/wallet/rpc/wallet.cpp (2)
src/wallet/rpc/addresses.cpp (2)
walletdisplayaddress(670-708)walletdisplayaddress(670-670)src/node/interfaces.cpp (1)
ENABLE_EXTERNAL_SIGNER(530-551)
src/wallet/wallet.h (6)
src/external_signer.cpp (2)
DisplayAddress(62-65)DisplayAddress(62-62)src/wallet/wallet.cpp (4)
DisplayAddress(2505-2518)DisplayAddress(2505-2505)FillPSBT(2032-2081)FillPSBT(2032-2032)src/wallet/external_signer_scriptpubkeyman.cpp (4)
DisplayAddress(52-61)DisplayAddress(52-52)FillPSBT(64-85)FillPSBT(64-64)src/wallet/interfaces.cpp (12)
dest(210-214)dest(210-210)dest(223-226)dest(223-223)dest(227-230)dest(227-227)dest(231-249)dest(231-234)dest(264-268)dest(264-264)dest(269-273)dest(269-269)src/wallet/scriptpubkeyman.cpp (4)
FillPSBT(734-778)FillPSBT(734-734)FillPSBT(2288-2355)FillPSBT(2288-2288)src/psbt.cpp (1)
PartiallySignedTransaction(16-20)
src/wallet/wallet.cpp (4)
src/util/translation.h (1)
Untranslated(48-48)src/external_signer.cpp (2)
DisplayAddress(62-65)DisplayAddress(62-62)src/wallet/external_signer_scriptpubkeyman.cpp (4)
DisplayAddress(52-61)DisplayAddress(52-52)GetExternalSigner(42-50)GetExternalSigner(42-42)src/wallet/interfaces.cpp (12)
dest(210-214)dest(210-210)dest(223-226)dest(223-223)dest(227-230)dest(227-227)dest(231-249)dest(231-234)dest(264-268)dest(264-264)dest(269-273)dest(269-269)
src/external_signer.h (3)
src/external_signer.cpp (11)
ExternalSigner(17-17)NetworkArg(19-22)NetworkArg(19-19)Enumerate(24-60)Enumerate(24-24)DisplayAddress(62-65)DisplayAddress(62-62)GetDescriptors(67-70)GetDescriptors(67-67)SignTransaction(72-117)SignTransaction(72-72)src/wallet/wallet.cpp (6)
DisplayAddress(2505-2518)DisplayAddress(2505-2505)SignTransaction(1998-2015)SignTransaction(1998-1998)SignTransaction(2017-2030)SignTransaction(2017-2017)src/wallet/external_signer_scriptpubkeyman.cpp (2)
DisplayAddress(52-61)DisplayAddress(52-52)
src/qt/createwalletdialog.h (2)
src/interfaces/node.h (2)
interfaces(48-411)ExternalSigner(161-168)src/qt/createwalletdialog.cpp (4)
setSigners(115-134)setSigners(115-115)CreateWalletDialog(17-108)CreateWalletDialog(110-113)
🪛 Flake8 (7.3.0)
test/functional/wallet_signer.py
[error] 106-106: continuation line under-indented for visual indent
(E128)
[error] 107-107: closing bracket does not match visual indentation
(E124)
[error] 126-126: continuation line missing indentation or outdented
(E122)
test/functional/rpc_signer.py
[error] 52-52: continuation line under-indented for visual indent
(E128)
[error] 57-57: continuation line under-indented for visual indent
(E128)
[error] 63-63: continuation line under-indented for visual indent
(E128)
[error] 69-69: continuation line under-indented for visual indent
(E128)
test/functional/mocks/signer.py
[error] 21-21: indentation is not a multiple of 4
(E111)
[error] 96-96: indentation is not a multiple of 4
(E111)
🪛 LanguageTool
doc/external-signer.md
[style] ~70-~70: Consider a more concise word here.
Context: ...ue, "txid": } ``` ## Signer API In order to be compatible with Dash Core any signer...
(IN_ORDER_TO_PREMIUM)
[grammar] ~167-~167: Use a hyphen to join words.
Context: ... support address types, in a BIP44/49/84 compatible manner. The `walletdisplayad...
(QB_NEW_EN_HYPHEN)
🪛 markdownlint-cli2 (0.18.1)
doc/external-signer.md
14-14: Dollar signs used before commands without showing output
(MD014, commands-show-output)
25-25: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
40-40: Dollar signs used before commands without showing output
(MD014, commands-show-output)
48-48: Dollar signs used before commands without showing output
(MD014, commands-show-output)
49-49: Dollar signs used before commands without showing output
(MD014, commands-show-output)
59-59: Dollar signs used before commands without showing output
(MD014, commands-show-output)
80-80: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
98-98: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
115-115: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
122-122: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
141-141: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
147-147: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🪛 Ruff (0.14.0)
test/functional/test_framework/test_framework.py
1109-1109: Avoid specifying long messages outside the exception class
(TRY003)
test/functional/mocks/signer.py
20-20: Unused function argument: args
(ARG001)
test/functional/mocks/invalid_signer.py
20-20: Unused function argument: args
(ARG001)
| @@ -25,4 +25,4 @@ export RUN_FUNCTIONAL_TESTS=false | |||
| export GOAL="install" | |||
| # -Wno-psabi is to disable ABI warnings: "note: parameter passing for argument of type ... changed in GCC 7.1" | |||
| # This could be removed once the ABI change warning does not show up by default | |||
| export BITCOIN_CONFIG="--enable-reduce-exports CXXFLAGS=-Wno-psabi --with-boost-process" | |||
| export BITCOIN_CONFIG="--enable-reduce-exports CXXFLAGS=-Wno-psabi" | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Consider the scope of CI changes in this PR.
This file removes the --with-boost-process flag from the build configuration. The coding guidelines state: "Unless specifically prompted, avoid making changes to the ci directory."
Given the PR's scope issues, consider isolating CI configuration changes into a separate, focused PR.
As per coding guidelines.
🤖 Prompt for AI Agents
ci/test/00_setup_env_arm.sh around line 28: this change removed the
--with-boost-process flag from the CI build config (export
BITCOIN_CONFIG="--enable-reduce-exports CXXFLAGS=-Wno-psabi"), which violates
the guideline to avoid unsolicited ci/ changes; either restore the original
BITCOIN_CONFIG to include --with-boost-process and keep CI untouched in this PR,
or revert this file and move the CI configuration change into a separate focused
PR that documents and justifies the change.
| @@ -12,4 +12,4 @@ export TEST_RUNNER_EXTRA="--timeout-factor=4" # Increase timeout because saniti | |||
| export FUNCTIONAL_TESTS_CONFIG="--exclude wallet_multiwallet.py" # Temporarily suppress ASan heap-use-after-free (see issue #14163) | |||
| export RUN_BENCH=true | |||
| export GOAL="install" | |||
| export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=qt5 CPPFLAGS=-DDEBUG_LOCKORDER --with-sanitizers=address,integer,undefined CC=clang CXX=clang++ --with-boost-process" | |||
| export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=qt5 CPPFLAGS=-DDEBUG_LOCKORDER --with-sanitizers=address,integer,undefined CC=clang CXX=clang++" | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Consider the scope of CI changes in this PR.
This file removes the --with-boost-process flag from the build configuration. While the change itself is straightforward, the coding guidelines state: "Unless specifically prompted, avoid making changes to the ci directory."
Given the PR's catastrophic validation failures (1,156 files changed, 32,244 insertions, 31,996 deletions), consider whether CI changes should be isolated into a separate, focused PR with clear justification and Bitcoin commit references.
As per coding guidelines.
🤖 Prompt for AI Agents
ci/test/00_setup_env_native_asan.sh lines 15-15: this PR removed the
--with-boost-process build flag from the CI script, which violates the guideline
to avoid changes in the ci directory unless explicitly prompted; restore the
original BITCOIN_CONFIG line (re-adding --with-boost-process) in this PR or else
revert this file here and create a separate, focused PR that only modifies CI
with a clear justification and references to the upstream Bitcoin commits that
mandate the change, and include in that PR a brief rationale and test plan so
reviewers can evaluate CI-only changes independently.
doc/external-signer.md
Outdated
|
|
||
| ## Example usage | ||
|
|
||
| The following example is based on the [HWI](https://github.com/bitcoin-core/HWI) tool. Version 2.0 or newer is required. Although this tool is hosted under the Dash Core GitHub organization and maintained by Dash Core developers, it should be used with caution. It is considered experimental and has far less review than Dash Core itself. Be particularly careful when running tools such as these on a computer with private keys on it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix HWI org reference
HWI is hosted under the bitcoin-core organization, not Dash Core. Update the wording to avoid confusion.
🤖 Prompt for AI Agents
In doc/external-signer.md around line 7, the paragraph incorrectly states HWI is
hosted under the Dash Core GitHub organization; change the wording to say HWI is
hosted under the bitcoin-core organization (link remains the same) to avoid
confusion and clarify maintainership.
| // First fill transaction with our data without signing, | ||
| // so external signers are not asked sign more than once. | ||
| bool complete; | ||
| (void)pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, false, true); | ||
| const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, true, false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initialize ‘complete’ before first FillPSBT and document the ignored error.
Initialize to a known value to avoid any potential read-before-write on error paths inside FillPSBT, and make the intentional error ignore explicit.
- bool complete;
- (void)pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, false, true);
+ bool complete = false;
+ // Intentionally pre-fill without signing so external signers are prompted only once.
+ // Ignore the return value here; the subsequent signing call will surface errors.
+ (void)pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, /*sign=*/false, /*bip32derivs=*/true);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // First fill transaction with our data without signing, | |
| // so external signers are not asked sign more than once. | |
| bool complete; | |
| (void)pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, false, true); | |
| const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, true, false); | |
| // First fill transaction with our data without signing, | |
| // so external signers are not asked sign more than once. | |
| bool complete = false; | |
| // Intentionally pre-fill without signing so external signers are prompted only once. | |
| // Ignore the return value here; the subsequent signing call will surface errors. | |
| (void)pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, /*sign=*/false, /*bip32derivs=*/true); | |
| const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, true, false); |
🤖 Prompt for AI Agents
In src/wallet/rpc/spend.cpp around lines 907 to 911, initialize the local bool
complete to a known value (e.g., bool complete = false;) before the first call
to pwallet->FillPSBT to avoid any potential read-before-write inside FillPSBT,
and replace the current silent cast-to-void with an explicit comment documenting
that the return/error from the first FillPSBT is intentionally ignored (e.g.,
"/* intentionally ignore result; only used to populate PSBT for external signers
*/") so readers and static analyzers understand the intent.
| {"external_signer", RPCArg::Type::BOOL, RPCArg::Default{false}, "Use an external signer such as a hardware wallet. Requires -signer to be configured. Wallet creation will fail if keys cannot be fetched. Requires disable_private_keys and descriptors set to true."}, | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New createwallet arg external_signer: tighten invariants
Enforce at RPC boundary: when external_signer=true require descriptors=true, disable_private_keys=true, and a non-empty -signer. Otherwise fail fast with a clear error.
Apply this minimal guard before wallet creation:
@@
- if (!request.params[7].isNull() && request.params[7].get_bool()) {
+ if (!request.params[7].isNull() && request.params[7].get_bool()) {
#ifdef ENABLE_EXTERNAL_SIGNER
- flags |= WALLET_FLAG_EXTERNAL_SIGNER;
+ if (!(flags & WALLET_FLAG_DESCRIPTORS)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Descriptor support must be enabled when using an external signer");
+ }
+ if (!(flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Private keys must be disabled when using an external signer");
+ }
+ if (gArgs.GetArg("-signer", "").empty()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "External signer requires -signer to be configured");
+ }
+ flags |= WALLET_FLAG_EXTERNAL_SIGNER;
#else
throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without external signing support (required for external signing)");
#endif
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/wallet/rpc/wallet.cpp around lines 628-629, enforce at the RPC boundary
that if the new createwallet argument external_signer is true then descriptors
must be true, disable_private_keys must be true, and the configured -signer
string must be non-empty; if any of these invariants do not hold, return an RPC
error immediately with a clear message explaining which requirement failed (e.g.
"external_signer requires descriptors=true, disable_private_keys=true, and
-signer to be set"). Add this minimal guard check immediately before wallet
creation so the RPC fails fast rather than proceeding to wallet creation.
| def perform_pre_checks(): | ||
| mock_result_path = os.path.join(os.getcwd(), "mock_result") | ||
| if(os.path.isfile(mock_result_path)): | ||
| with open(mock_result_path, "r", encoding="utf8") as f: | ||
| mock_result = f.read() | ||
| if mock_result[0]: | ||
| sys.stdout.write(mock_result[2:]) | ||
| sys.exit(int(mock_result[0])) | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard empty mock_result and check exit code explicitly
Indexing mock_result[0] will crash on empty files; also make the exit test explicit.
def perform_pre_checks():
mock_result_path = os.path.join(os.getcwd(), "mock_result")
if(os.path.isfile(mock_result_path)):
with open(mock_result_path, "r", encoding="utf8") as f:
mock_result = f.read()
- if mock_result[0]:
- sys.stdout.write(mock_result[2:])
- sys.exit(int(mock_result[0]))
+ if mock_result:
+ code_char = mock_result[0]
+ if code_char != '0':
+ sys.stdout.write(mock_result[2:])
+ sys.exit(int(code_char))🤖 Prompt for AI Agents
In test/functional/mocks/invalid_signer.py around lines 11 to 19: after reading
mock_result, first guard for an empty file (if not mock_result: return or exit
early) to avoid indexing; then validate the first character is an explicit digit
(use .isdigit()) before converting to int; write the rest of the file to stdout
after consuming that digit (use mock_result[1:] or strip a known separator if
present) and call sys.exit(int(mock_result[0])) so the exit code conversion is
explicit and safe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/test/system_tests.cpp (1)
39-46: Fix invalid string literal and JSON quoting on non‑Windows.The literal at Line 41 is unterminated and will not compile. Escape inner quotes.
-#else - const UniValue result = RunCommandParseJSON("echo \"{\"success\": true}\""); -#endif +#else + const UniValue result = RunCommandParseJSON("echo \"{\\\"success\\\": true}\""); +#endif
♻️ Duplicate comments (23)
src/Makefile.test.include (1)
42-49: Unconditional wallet linkage breaks --disable-wallet builds.Linking $(LIBBITCOIN_WALLET) in FUZZ_SUITE_LD_COMMON unconditionally causes linker errors when wallet is disabled. Move it under ENABLE_WALLET.
@@ FUZZ_SUITE_LD_COMMON = \ $(LIBTEST_FUZZ) \ $(LIBTEST_UTIL) \ $(LIBBITCOIN_NODE) \ - $(LIBBITCOIN_WALLET) \ $(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_UTIL) \ @@ if ENABLE_WALLET @@ -FUZZ_SUITE_LD_COMMON +=\ +FUZZ_SUITE_LD_COMMON += \ + $(LIBBITCOIN_WALLET) \ $(SQLITE_LIBS) \ $(BDB_LIBS)Also applies to: 223-226
src/wallet/rpc/spend.cpp (1)
907-911: Initialize ‘complete’ before first FillPSBT.Avoid potential read-before-write on error paths; keep the explanatory comment.
- // First fill transaction with our data without signing, - // so external signers are not asked sign more than once. - bool complete; - (void)pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, false, true); + // First fill transaction with our data without signing, + // so external signers are not asked sign more than once. + bool complete = false; + (void)pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, /*sign=*/false, /*bip32derivs=*/true);configure.ac (1)
327-331: Make external‑signer truly ‘auto’ and degrade gracefully when Boost.Process is absent.Default is currently “yes” (contradicts help) and forces hard failure without Boost.Process. Switch to auto-detect and only error if explicitly requested. Also drop unnecessary AC_SUBST.
@@ -AC_ARG_ENABLE([external-signer], - [AS_HELP_STRING([--enable-external-signer],[compile external signer support (default is auto, requires Boost::Process)])], - [use_external_signer=$enableval], - [use_external_signer=yes]) +AC_ARG_ENABLE([external-signer], + [AS_HELP_STRING([--enable-external-signer],[compile external signer support (default: auto; requires Boost::Process)])], + [use_external_signer=$enableval], + [use_external_signer=auto]) @@ - dnl Opt-in to Boost Process if external signer support is requested - if test "$use_external_signer" != no; then - AC_MSG_CHECKING(for Boost Process) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <boost/process.hpp>]], - [[ boost::process::child* child = new boost::process::child; delete child; ]])], - [ AC_MSG_RESULT(yes) - AC_DEFINE([ENABLE_EXTERNAL_SIGNER],,[define if external signer support is enabled]) - ], - [ AC_MSG_ERROR([Boost::Process is required for external signer support, but not available!])] - ) - fi + dnl Probe Boost.Process when enabled explicitly or in auto mode + if test "$use_external_signer" = yes || test "$use_external_signer" = auto; then + AC_MSG_CHECKING([for Boost.Process]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <boost/process.hpp>]], + [[ boost::process::child c((char*)""); ]])], + [ AC_MSG_RESULT([yes]) + use_external_signer=yes + AC_DEFINE([ENABLE_EXTERNAL_SIGNER],[1],[Define if external signer support is enabled]) ], + [ AC_MSG_RESULT([no]) + if test "$use_external_signer" = yes; then + AC_MSG_ERROR([--enable-external-signer requires Boost.Process]) + else + use_external_signer=no + fi ]) + fi @@ -AM_CONDITIONAL([ENABLE_EXTERNAL_SIGNER], [test "$use_external_signer" = "yes"]) +AM_CONDITIONAL([ENABLE_EXTERNAL_SIGNER], [test "$use_external_signer" = yes]) @@ -AC_SUBST(ENABLE_EXTERNAL_SIGNER) +# not needed; use AM_CONDITIONAL and C/C++ macroAlso applies to: 1556-1586, 1591-1592, 1978-1979
ci/test/00_setup_env_native_asan.sh (1)
15-15: Consider isolating CI configuration changes.The coding guidelines state: "Unless specifically prompted, avoid making changes to the ci directory." Given the PR's substantial scope, CI configuration changes may be better suited for a separate, focused PR.
As per coding guidelines.
ci/test/00_setup_env_native_nowallet.sh (1)
14-14: Consider isolating CI configuration changes.The coding guidelines state: "Unless specifically prompted, avoid making changes to the ci directory." Given the PR's substantial scope, CI configuration changes may be better suited for a separate, focused PR.
As per coding guidelines.
src/qt/receiverequestdialog.cpp (1)
99-101: Fix signal connection leak in setInfo.The
connect()call creates a new signal-slot connection each timesetInfo()is invoked. If called multiple times, clicking the Verify button will triggerdisplayAddress()multiple times.Apply one of these fixes:
Option 1 (preferred): Move connection to constructor
Move the signal connection to the constructor so it's only connected once:ReceiveRequestDialog::ReceiveRequestDialog(QWidget *parent) : QDialog(parent, GUIUtil::dialog_flags), ui(new Ui::ReceiveRequestDialog), model(nullptr) { ui->setupUi(this); GUIUtil::updateFonts(); GUIUtil::handleCloseWindowShortcut(this); + + connect(ui->btnVerify, &QPushButton::clicked, this, [this] { + if (model) { + model->displayAddress(info.address.toStdString()); + } + }); }Then remove the connect call from
setInfo():ui->btnVerify->setVisible(model->wallet().hasExternalSigner()); - - connect(ui->btnVerify, &QPushButton::clicked, [this] { - model->displayAddress(info.address.toStdString()); - }); }Option 2: Use Qt::UniqueConnection flag
- connect(ui->btnVerify, &QPushButton::clicked, [this] { + connect(ui->btnVerify, &QPushButton::clicked, this, [this] { model->displayAddress(info.address.toStdString()); - }); + }, Qt::UniqueConnection);src/wallet/rpc/addresses.cpp (1)
669-710: RPC handler implementation is correct, but underlying DisplayAddress has known issue.The RPC endpoint properly validates the address and checks the return value from
DisplayAddress. However, as noted in previous reviews, the underlying implementation insrc/wallet/external_signer_scriptpubkeyman.cppdoes not inspect the result ofsigner.DisplayAddress()and unconditionally returnstrue, which could mask hardware device failures.doc/external-signer.md (1)
7-7: Fix HWI org and wording.HWI is under the bitcoin-core org, not Dash. Also reword to avoid implying Dash maintainership.
-The following example is based on the [HWI](https://github.com/dashpay/HWI) tool. Version 2.0 or newer is required. Although this tool is hosted under the Dash Core GitHub organization and maintained by Dash Core developers, it should be used with caution. +The following example is based on the [HWI](https://github.com/bitcoin-core/HWI) tool. Version 2.0 or newer is required. Although this tool is hosted under the bitcoin-core GitHub organization, it should be used with caution.src/wallet/external_signer_scriptpubkeyman.cpp (1)
52-61: Guard null provider and handle DisplayAddress errors.Current code may dereference a null provider and ignores failures from the external command.
bool ExternalSignerScriptPubKeyMan::DisplayAddress(const CScript scriptPubKey, const ExternalSigner &signer) const { // TODO: avoid the need to infer a descriptor from inside a descriptor wallet - auto provider = GetSolvingProvider(scriptPubKey); - auto descriptor = InferDescriptor(scriptPubKey, *provider); - - signer.DisplayAddress(descriptor->ToString()); - // TODO inspect result - return true; + auto provider = GetSolvingProvider(scriptPubKey); + if (!provider) { + return false; + } + auto descriptor = InferDescriptor(scriptPubKey, *provider); + if (!descriptor) { + return false; + } + try { + (void)signer.DisplayAddress(descriptor->ToString()); + return true; + } catch (const std::exception&) { + return false; + } }test/functional/mocks/invalid_signer.py (1)
11-19: Harden mock pre-checks, fix duplicate var, and require --account
- Guard empty mock_result, validate exit digit, and only treat nonzero as error.
- Remove duplicate xpub assignment typo.
- Make --account required to avoid None concatenation errors.
- Silence ARG001 by marking enumerate’s arg unused.
def perform_pre_checks(): mock_result_path = os.path.join(os.getcwd(), "mock_result") if(os.path.isfile(mock_result_path)): with open(mock_result_path, "r", encoding="utf8") as f: mock_result = f.read() - if mock_result[0]: - sys.stdout.write(mock_result[2:]) - sys.exit(int(mock_result[0])) + if mock_result: + code_char = mock_result[0] + payload = mock_result[1:] if len(mock_result) > 1 else "" + if code_char.isdigit(): + code = int(code_char) + if code != 0: + sys.stdout.write(payload.lstrip()) + sys.exit(code) - xpub_pkh = "xpub6CRhJvXV8x2AKWvqi1ZSMFU6cbxzQiYrv3dxSUXCawjMJ1JzpqVsveH4way1yCmJm29KzH1zrVZmVwes4Qo6oXVE1HFn4fdiKrYJngqFFc6" + # legacy pkh xpub (unused here, kept for reference if needed) + # xpub_pkh_legacy = "xpub6CRhJvXV8x2AKWvqi1ZSMFU6cbxzQiYrv3dxSUXCawjMJ1JzpqVsveH4way1yCmJm29KzH1zrVZmVwes4Qo6oXVE1HFn4fdiKrYJngqFFc6" xpub_sh = "xpub6CoNoq3Tg4tGSpom2BSwL42gy864KHo3TXkHxLxBbhvCkgmdVXADQmiHbLZhX3Me1cYhRx7s25Lpm4LnT5zu395ANHsXB2QvT9tqJDAibTN" xpub_pkh = "xpub6DUcLgY1DfgDy2RV6q4djwwsLitaoZDumbribqrR8mP78fEtgZa1XEsqT5MWQ7gwLwKsTQPT28XLoVE5A97rDNTwMXjmzPaNijoCApCbWvp" -parser_enumerate = subparsers.add_parser('enumerate', help='list available signers') -parser_enumerate.set_defaults(func=enumerate) +parser_enumerate = subparsers.add_parser('enumerate', help='list available signers') +parser_enumerate.set_defaults(func=enumerate) parser_getdescriptors = subparsers.add_parser('getdescriptors') parser_getdescriptors.set_defaults(func=getdescriptors) -parser_getdescriptors.add_argument('--account', metavar='account') +parser_getdescriptors.add_argument('--account', metavar='account', required=True) -def enumerate(args): +def enumerate(_args): sys.stdout.write(json.dumps([{"fingerprint": "b3c19bfc", "type": "trezor", "model": "trezor_t"}, {"fingerprint": "00000002"}]))Also applies to: 24-27, 52-55
src/qt/sendcoinsdialog.cpp (3)
901-913: Apply same watch‑only fix in updateCoinControlState()Keep behavior consistent across code paths.
- // Include watch-only for wallets without private key - m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner(); + // Include watch-only for wallets without private key or with external signer + m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled() || model->wallet().hasExternalSigner();
769-773: Assign external balance when showing “External balance”Balance remains 0 in this branch. Use watch‑only balance for external-signer wallets.
- if (model->wallet().hasExternalSigner()) { - ui->labelBalanceName->setText(tr("External balance:")); + if (model->wallet().hasExternalSigner()) { + balance = balances.watch_only_balance; + ui->labelBalanceName->setText(tr("External balance:"));
855-859: Allow watch‑only for external signer walletsCoin control must include watch‑only UTXOs for external-signer wallets.
- m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner(); + m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled() || model->wallet().hasExternalSigner();src/node/interfaces.cpp (1)
530-551: Harden listExternalSigners: catch enumerate errors; fix typos; minor cleanupsWrap Enumerate() to avoid crashing GUI/RPC on malformed signer output; log and return empty. Also fix comment typos and use .empty().
#ifdef ENABLE_EXTERNAL_SIGNER - std::vector<ExternalSigner> signers = {}; - const std::string command = gArgs.GetArg("-signer", ""); - if (command == "") return {}; - ExternalSigner::Enumerate(command, signers, Params().NetworkIDString()); + std::vector<ExternalSigner> signers; + const std::string command = gArgs.GetArg("-signer", ""); + if (command.empty()) return {}; + try { + ExternalSigner::Enumerate(command, signers, Params().NetworkIDString()); + } catch (const std::exception& e) { + LogPrintf("External signer enumerate failed: %s\n", e.what()); + return {}; + } @@ #else - // This result is undistinguisable from a succesful call that returns + // This result is indistinguishable from a successful call that returns // no signers. For the current GUI this doesn't matter, because the wallet // creation dialog disables the external signer checkbox in both // cases. The return type could be changed to std::optional<std::vector> // (or something that also includes error messages) if this distinction // becomes important. return {}; #endif // ENABLE_EXTERNAL_SIGNERsrc/external_signer.h (3)
54-59: Make GetDescriptors constIt doesn’t mutate state; mark const to improve API clarity.
- UniValue GetDescriptors(const int account); + UniValue GetDescriptors(int account) const;
60-64: Make SignTransaction constMethod doesn’t modify members; declare const.
- bool SignTransaction(PartiallySignedTransaction& psbt, std::string& error); + bool SignTransaction(PartiallySignedTransaction& psbt, std::string& error) const;
42-53: Safer command construction (argv, not shell strings)Public API embeds user-derived strings (descriptor, fingerprint) into shell command lines downstream. Switch RunCommandParseJSON to an argv-based exec (no shell), or add a robust escaping layer before concatenation. This prevents injection if unexpected characters appear.
src/external_signer.cpp (5)
19-22: Quote the chain flag to avoid shell splittingWrap chain value in quotes when building the argument.
- return " --chain " + m_chain; + return " --chain \"" + m_chain + "\"";
47-51: Don’t abort enumeration on a duplicateUse continue, not break, to keep processing remaining signers. Also avoid shadowing the
signername in the inner loop.- bool duplicate = false; - for (const ExternalSigner& signer : signers) { - if (signer.m_fingerprint.compare(fingerprintStr) == 0) duplicate = true; - } - if (duplicate) break; + bool duplicate = false; + for (const ExternalSigner& existing : signers) { + if (existing.m_fingerprint == fingerprintStr) { duplicate = true; break; } + } + if (duplicate) continue;
62-65: Command construction uses raw concatenation (injection risk)
descriptorandm_fingerprintare interpolated into a shell string executed by RunCommandParseJSON. Migrate to an argv-basedRunCommandParseJSONoverload (no shell), or strictly escape inputs. Short-term, ensure inputs are validated/escaped.
67-70: Same injection concern in GetDescriptorsAvoid embedding
m_fingerprintdirectly into a shell string; use argv-based exec or strict escaping.
72-96: Same injection concern in SignTransaction command
m_fingerprintis embedded in the executed command. Use argv-based exec; keep PSBT only on stdin. Also consider quoting/escaping until argv refactor is done.test/functional/wallet_signer.py (1)
119-132: Fix flake8 indentation and whitespace in list literalThe suggested indentation changes address E122, but the diff is incomplete—it omits the E231 whitespace fix required at line 122. The list
[0,1]must be updated to[0, 1].- result = mock_wallet.importdescriptors([{ + result = mock_wallet.importdescriptors([ + { "desc": "pkh([00000001/84'/1'/0']tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0/*)#6k3x80k9", "timestamp": 0, - "range": [0,1], + "range": [0, 1], "internal": False, "active": True }, { "desc": "pkh([00000001/84'/1'/0']tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/*)#tz5866xa", "timestamp": 0, "range": [0, 0], "internal": True, "active": True - }]) + } + ])
🧹 Nitpick comments (15)
src/util/system.cpp (1)
1397-1434: More robust stdout/stderr reads to handle multi-line JSON.Current code reads only one line from each stream; prefer consuming entire streams to avoid truncation. Safe as a non-functional improvement.
- std::string result; - std::string error; - std::getline(stdout_stream, result); - std::getline(stderr_stream, error); + std::string result{std::istreambuf_iterator<char>(stdout_stream), std::istreambuf_iterator<char>()}; + std::string error{std::istreambuf_iterator<char>(stderr_stream), std::istreambuf_iterator<char>()};ci/test/00_setup_env_win64.sh (1)
19-19: Consider isolating CI configuration changes.The coding guidelines state: "Unless specifically prompted, avoid making changes to the ci directory." Given the PR's substantial scope (1,156 files changed), CI configuration changes may be better suited for a separate, focused PR with clear upstream Bitcoin commit references.
As per coding guidelines.
doc/external-signer.md (1)
141-145: Add languages to code fences and drop leading “$” prompts.This satisfies MD040/MD014 and improves readability.
-```sh -$ dashd -signer=../HWI/hwi.py +```sh +dashd -signer=../HWI/hwi.py-``` -$ dash-cli enumeratesigners +```sh +dash-cli enumeratesigners { "signers": [ { "fingerprint": "c8df832a" } ] } -``` +```-```sh -$ dash-cli createwallet "hww" true true "" true true true +```sh +dash-cli createwallet "hww" true true "" true true true-```sh -$ dash-cli -rpcwallet=<wallet> getnewaddress -$ dash-cli -rpcwallet=<wallet> walletdisplayaddress <address> +```sh +dash-cli -rpcwallet=<wallet> getnewaddress +dash-cli -rpcwallet=<wallet> walletdisplayaddress <address>-```sh -$ dash-cli -rpcwallet=<wallet> sendtoaddress <address> <amount> +```sh +dash-cli -rpcwallet=<wallet> sendtoaddress <address> <amount>-``` +```json {"complete": true, "txid": <txid>}Also applies to: 147-149, 13-15, 25-33, 59-61, 80-87, 98-101, 115-118, 122-136 </blockquote></details> <details> <summary>src/interfaces/node.h (1)</summary><blockquote> `166-168`: **Make ExternalSigner::getName() const noexcept.** This improves API usability and signals non-throwing behavior. ```diff - virtual std::string getName() = 0; + virtual std::string getName() const noexcept = 0;src/Makefile.am (1)
910-929: Gate external signer sources behind ENABLE_EXTERNAL_SIGNER.To keep build lean when the feature is disabled, compile these only if enabled.
- external_signer.cpp \ +if ENABLE_EXTERNAL_SIGNER + external_signer.cpp \ @@ - rpc/external_signer.cpp \ + rpc/external_signer.cpp \ +endif- wallet/external_signer_scriptpubkeyman.cpp \ +if ENABLE_EXTERNAL_SIGNER + wallet/external_signer_scriptpubkeyman.cpp \ +endifHeaders may remain listed in BITCOIN_CORE_H as they don’t affect linking.
Please ensure configure.ac defines AM_CONDITIONAL(ENABLE_EXTERNAL_SIGNER, ...).
Also applies to: 650-651, 224-224
test/functional/mocks/signer.py (2)
11-19: Harden perform_pre_checks() against empty/short files.def perform_pre_checks(): mock_result_path = os.path.join(os.getcwd(), "mock_result") - if(os.path.isfile(mock_result_path)): - with open(mock_result_path, "r", encoding="utf8") as f: - mock_result = f.read() - if mock_result[0]: - sys.stdout.write(mock_result[2:]) - sys.exit(int(mock_result[0])) + if os.path.isfile(mock_result_path): + with open(mock_result_path, "r", encoding="utf8") as f: + first = f.readline().strip() + payload = f.read() + try: + code = int(first) + except Exception: + code = 1 + if payload: + sys.stdout.write(payload) + sys.exit(code)
69-76: Optional: silence unused-arg warning in enumerate().-parser_enumerate.set_defaults(func=enumerate) +parser_enumerate.set_defaults(func=lambda a: enumerate(a))Or prefix arg with “_args” to signal intentional unused.
src/wallet/external_signer_scriptpubkeyman.cpp (1)
42-50: Consider caching the selected signer.GetExternalSigner() enumerates on each call; caching the chosen device (fingerprint) would reduce latency and avoid device churn between calls.
Also applies to: 78-85
src/wallet/wallet.h (2)
30-31: Avoid heavy include from a public headerwallet.h doesn’t need <external_signer.h>; implementation includes it. Dropping it reduces compile times and coupling.
-#include <external_signer.h>
566-568: Make DisplayAddress const (pure query)The method doesn’t mutate state; making it const expands usability and matches other query methods.
- bool DisplayAddress(const CTxDestination& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool DisplayAddress(const CTxDestination& dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);Please confirm no callers rely on non-const; adjust the definition in wallet.cpp accordingly.
src/wallet/wallet.cpp (2)
2505-2518: Harden DisplayAddress against unexpected states and exceptionsAdd an early flag check and catch runtime errors from signer discovery/invocation to avoid unexpected crashes; return false and log instead.
bool CWallet::DisplayAddress(const CTxDestination& dest) { CScript scriptPubKey = GetScriptForDestination(dest); const auto spk_man = GetScriptPubKeyMan(scriptPubKey); if (spk_man == nullptr) { return false; } auto signer_spk_man = dynamic_cast<ExternalSignerScriptPubKeyMan*>(spk_man); if (signer_spk_man == nullptr) { return false; } - ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner(); - return signer_spk_man->DisplayAddress(scriptPubKey, signer); + if (!IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) return false; + try { + ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner(); + return signer_spk_man->DisplayAddress(scriptPubKey, signer); + } catch (const std::exception& e) { + WalletLogPrintf("DisplayAddress failed: %s\n", e.what()); + return false; + } }
3852-3880: Descriptor parsing from external source: consider requiring checksumsParsers currently allow descriptors without checksum (last arg false). Given input is external, prefer strictness to avoid subtle mismatches.
- std::unique_ptr<Descriptor> desc = Parse(desc_str, keys, desc_error, false); + std::unique_ptr<Descriptor> desc = Parse(desc_str, keys, desc_error, /*require_checksum=*/true);If mocks don’t include checksums yet, gate this under a flag or update tests accordingly.
src/external_signer.h (1)
27-28: Minor: drop const on value return typeReturning
const std::stringadds no benefit and inhibits moves. Preferstd::string NetworkArg() const;.- const std::string NetworkArg() const; + std::string NetworkArg() const;test/functional/wallet_signer.py (1)
103-108: Fix flake8 indentation for multiline callAlign continuation lines and closing paren.
- assert_raises_rpc_error(-1, 'RunCommandParseJSON error', - hww.walletdisplayaddress, address1 - ) + assert_raises_rpc_error( + -1, + 'RunCommandParseJSON error', + hww.walletdisplayaddress, + address1, + )src/qt/sendcoinsdialog.cpp (1)
229-240: UX copy: refine tooltip vs enabled stateIf a signer path is configured, enabling the button while showing “Connect your hardware wallet first.” is contradictory. Consider enabling with a “Ready to sign on device” tooltip; when unset, disable with the current “Set external signer…” message.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📥 Commits
Reviewing files that changed from the base of the PR and between e4004df4b14186f0f46ff69c95ea74a2fbf85d81 and a7c7318e407f4816f196d0ac649245d8d9e068db.
📒 Files selected for processing (68)
ci/test/00_setup_env_arm.sh(1 hunks)ci/test/00_setup_env_mac.sh(1 hunks)ci/test/00_setup_env_mac_native_x86_64.sh(1 hunks)ci/test/00_setup_env_native_asan.sh(1 hunks)ci/test/00_setup_env_native_fuzz.sh(1 hunks)ci/test/00_setup_env_native_multiprocess.sh(1 hunks)ci/test/00_setup_env_native_nowallet.sh(1 hunks)ci/test/00_setup_env_native_qt5.sh(1 hunks)ci/test/00_setup_env_native_tsan.sh(1 hunks)ci/test/00_setup_env_s390x.sh(1 hunks)ci/test/00_setup_env_win64.sh(1 hunks)configure.ac(7 hunks)doc/Doxyfile.in(1 hunks)doc/external-signer.md(1 hunks)src/Makefile.am(5 hunks)src/Makefile.test.include(1 hunks)src/dummywallet.cpp(1 hunks)src/external_signer.cpp(1 hunks)src/external_signer.h(1 hunks)src/interfaces/node.h(2 hunks)src/interfaces/wallet.h(2 hunks)src/node/interfaces.cpp(3 hunks)src/qt/createwalletdialog.cpp(4 hunks)src/qt/createwalletdialog.h(2 hunks)src/qt/forms/createwalletdialog.ui(2 hunks)src/qt/forms/optionsdialog.ui(1 hunks)src/qt/forms/receiverequestdialog.ui(1 hunks)src/qt/optionsdialog.cpp(3 hunks)src/qt/optionsmodel.cpp(3 hunks)src/qt/optionsmodel.h(1 hunks)src/qt/receiverequestdialog.cpp(1 hunks)src/qt/sendcoinsdialog.cpp(7 hunks)src/qt/walletcontroller.cpp(4 hunks)src/qt/walletmodel.cpp(2 hunks)src/qt/walletmodel.h(1 hunks)src/qt/walletmodeltransaction.cpp(1 hunks)src/qt/walletmodeltransaction.h(1 hunks)src/rpc/client.cpp(1 hunks)src/rpc/external_signer.cpp(1 hunks)src/rpc/register.h(2 hunks)src/test/system_tests.cpp(2 hunks)src/util/error.cpp(1 hunks)src/util/error.h(1 hunks)src/util/system.cpp(3 hunks)src/util/system.h(0 hunks)src/wallet/external_signer_scriptpubkeyman.cpp(1 hunks)src/wallet/external_signer_scriptpubkeyman.h(1 hunks)src/wallet/init.cpp(1 hunks)src/wallet/interfaces.cpp(2 hunks)src/wallet/rpc/addresses.cpp(1 hunks)src/wallet/rpc/backup.cpp(1 hunks)src/wallet/rpc/spend.cpp(1 hunks)src/wallet/rpc/wallet.cpp(6 hunks)src/wallet/scriptpubkeyman.h(2 hunks)src/wallet/wallet.cpp(9 hunks)src/wallet/wallet.h(5 hunks)src/wallet/walletdb.cpp(1 hunks)src/wallet/walletdb.h(1 hunks)src/wallet/walletutil.h(1 hunks)test/config.ini.in(1 hunks)test/functional/mocks/invalid_signer.py(1 hunks)test/functional/mocks/signer.py(1 hunks)test/functional/rpc_help.py(1 hunks)test/functional/rpc_signer.py(1 hunks)test/functional/test_framework/test_framework.py(1 hunks)test/functional/test_framework/test_node.py(1 hunks)test/functional/test_runner.py(1 hunks)test/functional/wallet_signer.py(1 hunks)
💤 Files with no reviewable changes (1)
- src/util/system.h
🚧 Files skipped from review as they are similar to previous changes (27)
- src/wallet/init.cpp
- src/rpc/external_signer.cpp
- src/qt/createwalletdialog.cpp
- src/dummywallet.cpp
- src/wallet/rpc/backup.cpp
- src/qt/optionsmodel.cpp
- src/wallet/walletutil.h
- ci/test/00_setup_env_arm.sh
- ci/test/00_setup_env_native_tsan.sh
- src/rpc/register.h
- src/rpc/client.cpp
- doc/Doxyfile.in
- ci/test/00_setup_env_native_qt5.sh
- src/qt/forms/receiverequestdialog.ui
- src/wallet/walletdb.h
- src/wallet/walletdb.cpp
- src/qt/walletmodeltransaction.cpp
- ci/test/00_setup_env_s390x.sh
- test/functional/rpc_help.py
- src/qt/forms/optionsdialog.ui
- test/functional/test_runner.py
- src/qt/createwalletdialog.h
- test/functional/test_framework/test_node.py
- src/qt/walletmodel.h
- src/wallet/scriptpubkeyman.h
- src/wallet/rpc/wallet.cpp
- src/wallet/interfaces.cpp
🧰 Additional context used
📓 Path-based instructions (5)
ci/**
📄 CodeRabbit inference engine (CLAUDE.md)
Unless specifically prompted, avoid making changes to the ci directory (continuous integration)
Files:
ci/test/00_setup_env_native_nowallet.shci/test/00_setup_env_mac.shci/test/00_setup_env_native_multiprocess.shci/test/00_setup_env_mac_native_x86_64.shci/test/00_setup_env_native_asan.shci/test/00_setup_env_win64.shci/test/00_setup_env_native_fuzz.sh
src/**/*.{cpp,h,cc,cxx,hpp}
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.{cpp,h,cc,cxx,hpp}: Dash Core C++ codebase must be written in C++20 and require at least Clang 16 or GCC 11.1
Dash uses unordered_lru_cache for efficient caching with LRU eviction
Files:
src/interfaces/node.hsrc/interfaces/wallet.hsrc/util/error.hsrc/qt/walletmodel.cppsrc/wallet/wallet.hsrc/external_signer.cppsrc/qt/walletcontroller.cppsrc/util/system.cppsrc/wallet/rpc/spend.cppsrc/util/error.cppsrc/qt/receiverequestdialog.cppsrc/wallet/external_signer_scriptpubkeyman.cppsrc/qt/optionsdialog.cppsrc/external_signer.hsrc/wallet/wallet.cppsrc/qt/sendcoinsdialog.cppsrc/qt/walletmodeltransaction.hsrc/wallet/external_signer_scriptpubkeyman.hsrc/qt/optionsmodel.hsrc/wallet/rpc/addresses.cppsrc/test/system_tests.cppsrc/node/interfaces.cpp
test/functional/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Functional tests should be written in Python and placed in test/functional/
Files:
test/functional/mocks/invalid_signer.pytest/functional/wallet_signer.pytest/functional/mocks/signer.pytest/functional/rpc_signer.pytest/functional/test_framework/test_framework.py
src/{test,wallet/test,qt/test}/**/*.{cpp,h,cc,cxx,hpp}
📄 CodeRabbit inference engine (CLAUDE.md)
Unit tests for C++ code should be placed in src/test/, src/wallet/test/, or src/qt/test/ and use Boost::Test or Qt 5 for GUI tests
Files:
src/test/system_tests.cpp
doc/**
📄 CodeRabbit inference engine (CLAUDE.md)
Unless specifically prompted, avoid making changes to the doc directory (documentation)
Files:
doc/external-signer.md
🧬 Code graph analysis (18)
src/interfaces/node.h (2)
src/external_signer.cpp (1)
ExternalSigner(17-17)src/external_signer.h (1)
ExternalSigner(18-64)
src/interfaces/wallet.h (2)
src/qt/walletmodel.cpp (2)
displayAddress(582-592)displayAddress(582-582)src/wallet/interfaces.cpp (12)
dest(209-213)dest(209-209)dest(222-225)dest(222-222)dest(226-229)dest(226-226)dest(230-248)dest(230-233)dest(263-267)dest(263-263)dest(268-272)dest(268-268)
src/qt/walletmodel.cpp (1)
src/wallet/interfaces.cpp (28)
dest(209-213)dest(209-209)dest(222-225)dest(222-222)dest(226-229)dest(226-226)dest(230-248)dest(230-233)dest(263-267)dest(263-263)dest(268-272)dest(268-268)m_wallet(157-160)m_wallet(165-165)m_wallet(174-174)m_wallet(175-175)m_wallet(181-181)m_wallet(182-182)m_wallet(442-445)m_wallet(446-449)m_wallet(521-521)m_wallet(522-522)m_wallet(523-523)m_wallet(524-524)m_wallet(525-525)m_wallet(526-526)m_wallet(531-531)m_wallet(591-591)
src/wallet/wallet.h (5)
src/external_signer.cpp (2)
DisplayAddress(62-65)DisplayAddress(62-62)src/wallet/wallet.cpp (4)
DisplayAddress(2505-2518)DisplayAddress(2505-2505)FillPSBT(2032-2081)FillPSBT(2032-2032)src/wallet/external_signer_scriptpubkeyman.cpp (4)
DisplayAddress(52-61)DisplayAddress(52-52)FillPSBT(64-85)FillPSBT(64-64)src/wallet/interfaces.cpp (12)
dest(209-213)dest(209-209)dest(222-225)dest(222-222)dest(226-229)dest(226-226)dest(230-248)dest(230-233)dest(263-267)dest(263-263)dest(268-272)dest(268-268)src/wallet/scriptpubkeyman.cpp (4)
FillPSBT(734-778)FillPSBT(734-734)FillPSBT(2288-2355)FillPSBT(2288-2288)
src/external_signer.cpp (3)
src/external_signer.h (1)
ExternalSigner(18-64)src/util/system.cpp (2)
RunCommandParseJSON(1397-1434)RunCommandParseJSON(1397-1397)src/wallet/external_signer_scriptpubkeyman.cpp (2)
DisplayAddress(52-61)DisplayAddress(52-52)
src/util/system.cpp (1)
src/node/interfaces.cpp (1)
ENABLE_EXTERNAL_SIGNER(530-551)
src/util/error.cpp (1)
src/util/translation.h (1)
Untranslated(48-48)
src/wallet/external_signer_scriptpubkeyman.cpp (3)
src/wallet/wallet.cpp (24)
batch(550-550)batch(815-815)batch(938-938)batch(1178-1178)batch(1257-1257)batch(1313-1313)batch(1358-1358)batch(1370-1370)batch(1391-1391)batch(1515-1515)batch(1660-1660)batch(1744-1744)batch(1793-1793)batch(2239-2239)batch(2300-2300)batch(2307-2307)DisplayAddress(2505-2518)DisplayAddress(2505-2505)GetSolvingProvider(3734-3738)GetSolvingProvider(3734-3734)GetSolvingProvider(3740-3748)GetSolvingProvider(3740-3740)FillPSBT(2032-2081)FillPSBT(2032-2032)src/wallet/scriptpubkeyman.cpp (16)
GetID(801-804)GetID(801-801)GetID(2375-2379)GetID(2375-2375)TopUp(1410-1413)TopUp(1410-1410)TopUp(1943-2004)TopUp(1943-1943)GetSolvingProvider(678-681)GetSolvingProvider(678-678)GetSolvingProvider(2231-2234)GetSolvingProvider(2231-2231)FillPSBT(734-778)FillPSBT(734-734)FillPSBT(2288-2355)FillPSBT(2288-2288)src/external_signer.cpp (4)
Enumerate(24-60)Enumerate(24-24)DisplayAddress(62-65)DisplayAddress(62-62)
test/functional/wallet_signer.py (6)
test/functional/test_framework/test_framework.py (6)
skip_test_if_missing_module(423-425)skip_test_if_missing_module(1477-1478)skip_if_no_external_signer(1106-1109)skip_if_no_wallet(1063-1071)restart_node(694-697)is_bdb_compiled(1147-1149)test/functional/rpc_signer.py (4)
mock_signer_path(21-26)skip_test_if_missing_module(38-39)set_mock_result(41-43)clear_mock_result(45-46)src/wallet/rpc/wallet.cpp (16)
res(591-591)createwallet(615-713)createwallet(615-615)getwalletinfo(152-276)getwalletinfo(152-152)setwalletflag(547-613)setwalletflag(547-547)getnewaddress(1098-1098)getaddressinfo(1097-1097)result(308-308)result(766-766)result(1088-1088)walletdisplayaddress(1108-1108)walletcreatefundedpsbt(1151-1151)walletprocesspsbt(1150-1150)send(1149-1149)test/functional/test_framework/test_node.py (2)
createwallet(884-889)get_wallet_rpc(363-369)src/wallet/rpc/addresses.cpp (10)
getnewaddress(17-56)getnewaddress(17-17)getaddressinfo(418-559)getaddressinfo(418-418)result(254-254)result(703-703)walletdisplayaddress(670-708)walletdisplayaddress(670-670)dest(382-382)dest(382-382)src/wallet/rpc/spend.cpp (11)
result(631-631)result(725-725)result(919-919)result(1014-1014)result(1164-1164)walletcreatefundedpsbt(1025-1171)walletcreatefundedpsbt(1025-1025)walletprocesspsbt(946-1023)walletprocesspsbt(946-946)send(732-944)send(732-732)
src/qt/optionsdialog.cpp (1)
src/node/interfaces.cpp (1)
ENABLE_EXTERNAL_SIGNER(530-551)
src/external_signer.h (3)
src/external_signer.cpp (11)
ExternalSigner(17-17)NetworkArg(19-22)NetworkArg(19-19)Enumerate(24-60)Enumerate(24-24)DisplayAddress(62-65)DisplayAddress(62-62)GetDescriptors(67-70)GetDescriptors(67-67)SignTransaction(72-117)SignTransaction(72-72)src/wallet/wallet.cpp (6)
DisplayAddress(2505-2518)DisplayAddress(2505-2505)SignTransaction(1998-2015)SignTransaction(1998-1998)SignTransaction(2017-2030)SignTransaction(2017-2017)src/wallet/external_signer_scriptpubkeyman.cpp (2)
DisplayAddress(52-61)DisplayAddress(52-52)
src/wallet/wallet.cpp (4)
src/util/translation.h (1)
Untranslated(48-48)src/external_signer.cpp (2)
DisplayAddress(62-65)DisplayAddress(62-62)src/wallet/external_signer_scriptpubkeyman.cpp (4)
DisplayAddress(52-61)DisplayAddress(52-52)GetExternalSigner(42-50)GetExternalSigner(42-42)src/wallet/interfaces.cpp (12)
dest(209-213)dest(209-209)dest(222-225)dest(222-222)dest(226-229)dest(226-226)dest(230-248)dest(230-233)dest(263-267)dest(263-263)dest(268-272)dest(268-268)
src/qt/walletmodeltransaction.h (1)
src/qt/walletmodeltransaction.cpp (2)
setWtx(29-32)setWtx(29-29)
src/wallet/external_signer_scriptpubkeyman.h (4)
src/wallet/scriptpubkeyman.h (6)
wallet(28-241)DescriptorScriptPubKeyMan(512-634)WalletStorage(34-234)bool(179-179)bool(218-218)bool(224-224)src/wallet/walletutil.h (1)
WalletDescriptor(70-103)src/wallet/external_signer_scriptpubkeyman.cpp (8)
SetupDescriptor(17-40)SetupDescriptor(17-17)GetExternalSigner(42-50)GetExternalSigner(42-42)DisplayAddress(52-61)DisplayAddress(52-52)FillPSBT(64-85)FillPSBT(64-64)src/wallet/wallet.cpp (4)
DisplayAddress(2505-2518)DisplayAddress(2505-2505)FillPSBT(2032-2081)FillPSBT(2032-2032)
test/functional/rpc_signer.py (4)
test/functional/test_framework/test_framework.py (1)
skip_if_no_external_signer(1106-1109)test/functional/test_framework/util.py (2)
assert_equal(69-74)assert_raises_rpc_error(132-148)test/functional/wallet_signer.py (5)
mock_signer_path(21-26)set_test_params(35-41)skip_test_if_missing_module(43-45)set_mock_result(47-49)clear_mock_result(51-52)src/rpc/external_signer.cpp (3)
enumeratesigners(16-59)enumeratesigners(16-16)result(54-54)
src/wallet/rpc/addresses.cpp (2)
src/wallet/rpc/util.cpp (2)
GetWalletForJSONRPCRequest(55-77)GetWalletForJSONRPCRequest(55-55)src/wallet/interfaces.cpp (18)
LOCK(249-258)LOCK(259-262)LOCK(290-294)LOCK(295-299)LOCK(352-360)LOCK(478-490)dest(209-213)dest(209-209)dest(222-225)dest(222-222)dest(226-229)dest(226-226)dest(230-248)dest(230-233)dest(263-267)dest(263-263)dest(268-272)dest(268-268)
src/test/system_tests.cpp (1)
src/node/interfaces.cpp (1)
ENABLE_EXTERNAL_SIGNER(530-551)
src/node/interfaces.cpp (1)
src/external_signer.cpp (2)
Enumerate(24-60)Enumerate(24-24)
🪛 Flake8 (7.3.0)
test/functional/wallet_signer.py
[error] 106-106: continuation line under-indented for visual indent
(E128)
[error] 107-107: closing bracket does not match visual indentation
(E124)
[error] 126-126: continuation line missing indentation or outdented
(E122)
test/functional/mocks/signer.py
[error] 21-21: indentation is not a multiple of 4
(E111)
[error] 96-96: indentation is not a multiple of 4
(E111)
test/functional/rpc_signer.py
[error] 52-52: continuation line under-indented for visual indent
(E128)
[error] 57-57: continuation line under-indented for visual indent
(E128)
[error] 63-63: continuation line under-indented for visual indent
(E128)
[error] 69-69: continuation line under-indented for visual indent
(E128)
🪛 LanguageTool
doc/external-signer.md
[style] ~70-~70: Consider a more concise word here.
Context: ...ue, "txid": } ``` ## Signer API In order to be compatible with Dash Core any signer...
(IN_ORDER_TO_PREMIUM)
[grammar] ~167-~167: Use a hyphen to join words.
Context: ... support address types, in a BIP44/49/84 compatible manner. The `walletdisplayad...
(QB_NEW_EN_HYPHEN)
🪛 markdownlint-cli2 (0.18.1)
doc/external-signer.md
14-14: Dollar signs used before commands without showing output
(MD014, commands-show-output)
25-25: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
40-40: Dollar signs used before commands without showing output
(MD014, commands-show-output)
48-48: Dollar signs used before commands without showing output
(MD014, commands-show-output)
49-49: Dollar signs used before commands without showing output
(MD014, commands-show-output)
59-59: Dollar signs used before commands without showing output
(MD014, commands-show-output)
80-80: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
98-98: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
115-115: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
122-122: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
141-141: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
147-147: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🪛 Ruff (0.14.1)
test/functional/mocks/invalid_signer.py
20-20: Unused function argument: args
(ARG001)
test/functional/mocks/signer.py
20-20: Unused function argument: args
(ARG001)
test/functional/test_framework/test_framework.py
1109-1109: Avoid specifying long messages outside the exception class
(TRY003)
🔇 Additional comments (23)
src/qt/optionsmodel.h (1)
76-76: LGTM!The addition of
ExternalSignerPathto theOptionIDenum is consistent with the external signer feature integration and properly typed as QString.src/util/error.h (1)
33-34: LGTM!The addition of
EXTERNAL_SIGNER_NOT_FOUNDandEXTERNAL_SIGNER_FAILEDto theTransactionErrorenum appropriately extends error handling for the external signer feature.test/config.ini.in (1)
26-26: LGTM!The addition of
ENABLE_EXTERNAL_SIGNERto the components section follows the established pattern for feature flags and appropriately enables external signer support in the test environment.src/qt/walletmodeltransaction.h (1)
30-31: LGTM!The addition of the
setWtx()method complements the existinggetWtx()accessor and follows standard setter patterns. The implementation is straightforward and correct.src/qt/forms/createwalletdialog.ui (1)
112-121: LGTM!The external signer checkbox is properly integrated into the Advanced Options section with clear tooltip guidance for users.
src/util/error.cpp (1)
36-39: LGTM!The new external signer error strings are clear and follow the established pattern in this function.
ci/test/00_setup_env_mac_native_x86_64.sh (1)
13-13: LGTM!Removal of
--with-boost-processaligns with the architectural shift to external signer support.ci/test/00_setup_env_mac.sh (1)
17-17: LGTM!Consistent with the removal of Boost.Process dependency across CI configurations.
src/interfaces/wallet.h (1)
154-155: LGTM!The new interface methods are well-positioned and follow the established pattern for wallet interface extensions. The placement near related functionality (address operations and key management) enhances code organization.
Also applies to: 294-295
test/functional/test_framework/test_framework.py (1)
1106-1117: LGTM!The new test framework methods follow the established pattern for feature detection and test skipping. The static analysis hint about message length is a false positive—the message is appropriately concise for a SkipTest exception.
src/qt/walletmodel.cpp (1)
582-592: LGTM!The
displayAddressimplementation properly handles errors with appropriate user feedback via QMessageBox. The try-catch block ensures graceful handling of runtime errors from the underlying wallet interface.doc/external-signer.md (2)
66-67: Double-check sendtoaddress output shape.Example shows {"complete": true, "txid": ...}. dash-cli sendtoaddress typically returns a txid string. Please confirm actual output and adjust the example accordingly.
27-36: Fix descriptor types in getdescriptors example.Wrapped SegWit must be sh(wpkh(...)), and native SegWit must be wpkh(...). Current text shows pkh in those slots.
- "pkh([00000001/44'/1'/" + args.account + "']" + xpub + "/0/*)#vt6w3l3j", - "sh(pkh([00000001/49'/1'/" + args.account + "']" + xpub + "/0/*))#az7uyg3n", - "pkh([00000001/84'/1'/" + args.account + "']" + xpub + "/0/*)#58ssc2nq" + "pkh([00000001/44'/1'/" + args.account + "']" + xpub + "/0/*)#vt6w3l3j", + "sh(wpkh([00000001/49'/1'/" + args.account + "']" + xpub + "/0/*))#az7uyg3n", + "wpkh([00000001/84'/1'/" + args.account + "']" + xpub + "/0/*)#58ssc2nq" @@ - "pkh([00000001/44'/1'/" + args.account + "']" + xpub + "/1/*)#all0v2p2", - "sh(pkh([00000001/49'/1'/" + args.account + "']" + xpub + "/1/*))#mpkel968", - "pkh([00000001/84'/1'/" + args.account + "']" + xpub + "/1/*)#9n439lrc" + "pkh([00000001/44'/1'/" + args.account + "']" + xpub + "/1/*)#all0v2p2", + "sh(wpkh([00000001/49'/1'/" + args.account + "']" + xpub + "/1/*))#mpkel968", + "wpkh([00000001/84'/1'/" + args.account + "']" + xpub + "/1/*)#9n439lrc"Likely an incorrect or invalid review comment.
src/interfaces/node.h (1)
234-236: API surface change: ensure all Node impls override listExternalSigners().Header adds a new pure virtual. Confirm all Node implementations are updated (node/interfaces.cpp shows an override).
src/qt/optionsdialog.cpp (1)
141-145: UI wiring looks good; verify OptionsModel plumbed.
- Gating and tooltip under !ENABLE_EXTERNAL_SIGNER are correct.
- Restart warning hook on externalSignerPath is consistent.
- Data mapper adds ExternalSignerPath.
Please confirm OptionsModel::ExternalSignerPath exists, persists, and is included in save/load.
Also applies to: 277-277, 344-345
ci/test/00_setup_env_native_fuzz.sh (1)
18-18: Confirm intent after dropping Boost.Process in fuzz config.Ensure this job doesn’t rely on Boost.Process-dependent bits and that sanitizer/fuzz targets compile without external signer support.
ci/test/00_setup_env_native_multiprocess.sh (1)
16-16: Verify external signer build behavior or explicitly disable it.The analysis confirms your concern is valid. When
--with-boost-processis removed:
- External signer defaults to enabled (line 330 of configure.ac:
use_external_signer=yes)- Build will check for
boost/process.hpp(line 1579:AC_COMPILE_IFELSEtest)- Build fails if Boost::Process is unavailable (line 1584:
AC_MSG_ERRORif missing)The CI job will either:
- Succeed if the CI environment has Boost::Process headers available, OR
- Fail at configure time if not
To clarify intent, either:
- Confirm the CI environment has
boost/process.hppand document this dependency, OR- Add
--disable-external-signerto explicitly skip this feature for this jobsrc/wallet/wallet.cpp (2)
315-335: Good preflight validation for external signer walletsEnforcing “disable private keys” and “descriptors” when WALLET_FLAG_EXTERNAL_SIGNER is set prevents invalid configurations. LGTM.
3805-3811: SPKM type selection via wallet flag is correctSelecting ExternalSignerScriptPubKeyMan when WALLET_FLAG_EXTERNAL_SIGNER is set is the right split. LGTM.
src/node/interfaces.cpp (2)
17-17: LGTM includeHeader include is fine; guarded usage below prevents ODR/link issues when feature is disabled.
402-411: LGTM: thin wrapperWrapper forwards name and moves the underlying signer; OK.
src/qt/sendcoinsdialog.cpp (2)
383-389: LGTM: clearer PSBT-only promptConditionally clarifies draft/PSBT flow when no external signer.
515-603: LGTM: safe PSBT flowFill without signing first, then sign if external signer, finalize/broadcast if complete; good error surfacing.
Please verify a tx-complete path also emits coinsSent or equivalent UI feedback when broadcast occurs via this branch.
| Follow the hardware manufacturers instructions for the initial device setup, as well as their instructions for creating a backup. Alternatively, for some devices, you can use the `setup`, `restore` and `backup` commands provided by [HWI](https://github.com/dashpay/HWI). | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update second HWI link to bitcoin-core.
-Alternatively, for some devices, you can use the `setup`, `restore` and `backup` commands provided by [HWI](https://github.com/dashpay/HWI).
+Alternatively, for some devices, you can use the `setup`, `restore` and `backup` commands provided by [HWI](https://github.com/bitcoin-core/HWI).📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Follow the hardware manufacturers instructions for the initial device setup, as well as their instructions for creating a backup. Alternatively, for some devices, you can use the `setup`, `restore` and `backup` commands provided by [HWI](https://github.com/dashpay/HWI). | |
| Follow the hardware manufacturers instructions for the initial device setup, as well as their instructions for creating a backup. Alternatively, for some devices, you can use the `setup`, `restore` and `backup` commands provided by [HWI](https://github.com/bitcoin-core/HWI). |
🤖 Prompt for AI Agents
In doc/external-signer.md around lines 19 to 20, the second HWI link points to
the dashpay/HWI repo; update that link to the bitcoin-core HWI repository by
replacing the URL https://github.com/dashpay/HWI with
https://github.com/bitcoin-core/HWI so the documentation references the official
Bitcoin Core HWI project.
|
|
||
| In order to be compatible with Dash Core any signer command should conform to the specification below. This specification is subject to change. Ideally a BIP should propose a standard so that other wallets can also make use of it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tighten wording.
-In order to be compatible with Dash Core any signer command should conform to the specification below.
+To be compatible with Dash Core, any signer command must conform to the specification below.🧰 Tools
🪛 LanguageTool
[style] ~70-~70: Consider a more concise word here.
Context: ...ue, "txid": } ``` ## Signer API In order to be compatible with Dash Core any signer...
(IN_ORDER_TO_PREMIUM)
🤖 Prompt for AI Agents
In doc/external-signer.md around lines 70 to 71, tighten the wording of the
paragraph to be more precise and directive: replace the tentative phrasing with
an active, concise sentence such as "To be compatible with Dash Core, signer
commands must conform to the specification below (subject to change);" and
follow with a separate clear sentence like "Ideally, a BIP should propose this
as a standard so other wallets can implement it." Ensure use of "must" for
compatibility requirement, remove vague words like "any" and "should" in the
compatibility clause, and split into two sentences for clarity.
| It then imports descriptors for all support address types, in a BIP44/49/84 compatible manner. | ||
|
|
||
| The `walletdisplayaddress` RPC reuses some code from `getaddressinfo` on the provided address and obtains the inferred descriptor. It then calls `<cmd> --fingerprint=00000000 displayaddress --desc=<descriptor>`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hyphenate compound modifier.
-It then imports descriptors for all support address types, in a BIP44/49/84 compatible manner.
+It then imports descriptors for all supported address types in a BIP44/49/84-compatible manner.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| It then imports descriptors for all support address types, in a BIP44/49/84 compatible manner. | |
| The `walletdisplayaddress` RPC reuses some code from `getaddressinfo` on the provided address and obtains the inferred descriptor. It then calls `<cmd> --fingerprint=00000000 displayaddress --desc=<descriptor>`. | |
| It then imports descriptors for all supported address types in a BIP44/49/84-compatible manner. | |
| The `walletdisplayaddress` RPC reuses some code from `getaddressinfo` on the provided address and obtains the inferred descriptor. It then calls `<cmd> --fingerprint=00000000 displayaddress --desc=<descriptor>`. |
🧰 Tools
🪛 LanguageTool
[grammar] ~167-~167: Use a hyphen to join words.
Context: ... support address types, in a BIP44/49/84 compatible manner. The `walletdisplayad...
(QB_NEW_EN_HYPHEN)
🤖 Prompt for AI Agents
In doc/external-signer.md around lines 167 to 169, hyphenate the compound
modifier "BIP44/49/84 compatible manner" to "BIP44/49/84-compatible manner" so
the adjective correctly modifies "manner"; update that phrase in the sentence
describing imported descriptors and leave the rest of the wording unchanged.
| #include <string> | ||
| #include <vector> | ||
|
|
||
| ExternalSigner::ExternalSigner(const std::string& command, const std::string chain, const std::string& fingerprint, const std::string name): m_command(command), m_chain(chain), m_fingerprint(fingerprint), m_name(name) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validate inputs early (fingerprint/chain)
Reject invalid m_fingerprint (non-hex or wrong length) and unknown m_chain in ctor to reduce downstream risk.
-ExternalSigner::ExternalSigner(const std::string& command, const std::string chain, const std::string& fingerprint, const std::string name): m_command(command), m_chain(chain), m_fingerprint(fingerprint), m_name(name) {}
+ExternalSigner::ExternalSigner(const std::string& command, const std::string chain, const std::string& fingerprint, const std::string name)
+ : m_command(command), m_chain(chain), m_fingerprint(fingerprint), m_name(name)
+{
+ if (!(m_chain == "main" || m_chain == "test" || m_chain == "regtest" || m_chain == "signet")) {
+ throw std::invalid_argument("ExternalSigner: invalid chain");
+ }
+ if (!IsHex(m_fingerprint) || m_fingerprint.size() != 8) {
+ throw std::invalid_argument("ExternalSigner: invalid fingerprint (expected 8 hex chars)");
+ }
+}🤖 Prompt for AI Agents
In src/external_signer.cpp around line 17, the constructor currently accepts any
fingerprint and chain; add early validation to reject invalid inputs by
verifying the fingerprint is hex and the expected length (e.g., 64 characters
for a 32-byte fingerprint or the project-specific length) and that the chain
string is one of the known/allowed values (e.g., "mainnet", "testnet", etc.). If
validation fails, throw a std::invalid_argument with a clear message indicating
whether the fingerprint format/length is wrong or the chain is unknown; perform
these checks before initializing member fields so invalid objects are never
created.
| std::vector<std::unique_ptr<interfaces::ExternalSigner>> signers; | ||
| try { | ||
| signers = node().listExternalSigners(); | ||
| } catch (const std::runtime_error& e) { | ||
| QMessageBox::critical(nullptr, tr("Can't list signers"), e.what()); | ||
| } | ||
| m_create_wallet_dialog->setSigners(signers); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move unique_ptr signers into the dialog to fix compilation
std::vector<std::unique_ptr<...>> is move-only. Passing by copy won’t compile. Move it.
- m_create_wallet_dialog->setSigners(signers);
+ m_create_wallet_dialog->setSigners(std::move(signers));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| std::vector<std::unique_ptr<interfaces::ExternalSigner>> signers; | |
| try { | |
| signers = node().listExternalSigners(); | |
| } catch (const std::runtime_error& e) { | |
| QMessageBox::critical(nullptr, tr("Can't list signers"), e.what()); | |
| } | |
| m_create_wallet_dialog->setSigners(signers); | |
| std::vector<std::unique_ptr<interfaces::ExternalSigner>> signers; | |
| try { | |
| signers = node().listExternalSigners(); | |
| } catch (const std::runtime_error& e) { | |
| QMessageBox::critical(nullptr, tr("Can't list signers"), e.what()); | |
| } | |
| m_create_wallet_dialog->setSigners(std::move(signers)); |
🤖 Prompt for AI Agents
In src/qt/walletcontroller.cpp around lines 296 to 303, the local variable
signers is a std::vector of move-only std::unique_ptrs and cannot be copied into
the dialog; change the call to transfer ownership by moving the vector into the
dialog (use std::move on signers when calling setSigners) so the dialog receives
the vector by move rather than by copy.
| "receive": [ | ||
| "pkh([00000001/44'/1'/" + args.account + "']" + xpub + "/0/*)#vt6w3l3j", | ||
| "sh(pkh([00000001/49'/1'/" + args.account + "']" + xpub + "/0/*))#az7uyg3n", | ||
| "pkh([00000001/84'/1'/" + args.account + "']" + xpub + "/0/*)#58ssc2nq" | ||
| ], | ||
| "internal": [ | ||
| "pkh([00000001/44'/1'/" + args.account + "']" + xpub + "/1/*)#all0v2p2", | ||
| "sh(pkh([00000001/49'/1'/" + args.account + "']" + xpub + "/1/*))#mpkel968", | ||
| "pkh([00000001/84'/1'/" + args.account + "']" + xpub + "/1/*)#9n439lrc" | ||
| ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct descriptor types in getdescriptors mock response.
Align with wrapped/native SegWit.
- "sh(pkh([00000001/49'/1'/" + args.account + "']" + xpub + "/0/*))#az7uyg3n",
- "pkh([00000001/84'/1'/" + args.account + "']" + xpub + "/0/*)#58ssc2nq"
+ "sh(wpkh([00000001/49'/1'/" + args.account + "']" + xpub + "/0/*))#az7uyg3n",
+ "wpkh([00000001/84'/1'/" + args.account + "']" + xpub + "/0/*)#58ssc2nq"
@@
- "sh(pkh([00000001/49'/1'/" + args.account + "']" + xpub + "/1/*))#mpkel968",
- "pkh([00000001/84'/1'/" + args.account + "']" + xpub + "/1/*)#9n439lrc"
+ "sh(wpkh([00000001/49'/1'/" + args.account + "']" + xpub + "/1/*))#mpkel968",
+ "wpkh([00000001/84'/1'/" + args.account + "']" + xpub + "/1/*)#9n439lrc"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "receive": [ | |
| "pkh([00000001/44'/1'/" + args.account + "']" + xpub + "/0/*)#vt6w3l3j", | |
| "sh(pkh([00000001/49'/1'/" + args.account + "']" + xpub + "/0/*))#az7uyg3n", | |
| "pkh([00000001/84'/1'/" + args.account + "']" + xpub + "/0/*)#58ssc2nq" | |
| ], | |
| "internal": [ | |
| "pkh([00000001/44'/1'/" + args.account + "']" + xpub + "/1/*)#all0v2p2", | |
| "sh(pkh([00000001/49'/1'/" + args.account + "']" + xpub + "/1/*))#mpkel968", | |
| "pkh([00000001/84'/1'/" + args.account + "']" + xpub + "/1/*)#9n439lrc" | |
| ] | |
| "receive": [ | |
| "pkh([00000001/44'/1'/" + args.account + "']" + xpub + "/0/*)#vt6w3l3j", | |
| "sh(wpkh([00000001/49'/1'/" + args.account + "']" + xpub + "/0/*))#az7uyg3n", | |
| "wpkh([00000001/84'/1'/" + args.account + "']" + xpub + "/0/*)#58ssc2nq" | |
| ], | |
| "internal": [ | |
| "pkh([00000001/44'/1'/" + args.account + "']" + xpub + "/1/*)#all0v2p2", | |
| "sh(wpkh([00000001/49'/1'/" + args.account + "']" + xpub + "/1/*))#mpkel968", | |
| "wpkh([00000001/84'/1'/" + args.account + "']" + xpub + "/1/*)#9n439lrc" | |
| ] |
🤖 Prompt for AI Agents
In test/functional/mocks/signer.py around lines 27 to 36, the mock
getdescriptors response is using incorrect descriptor types (e.g., sh(pkh(...))
and pkh(...) for SegWit xpubs); update the descriptors to use correct wrapped
and native SegWit forms: keep legacy 44' as pkh(...), change 49' to
sh(wpkh(...)) for P2SH-P2WPKH (wrapped SegWit), and change 84' to wpkh(...) for
native SegWit, applying the same replacements for both the "receive" (/0/*) and
"internal" (/1/*) entries so the mock matches real descriptor types.
| parser_signtx = subparsers.add_parser('signtx') | ||
| parser_signtx.add_argument('psbt', metavar='psbt') | ||
|
|
||
| parser_signtx.set_defaults(func=signtx) | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Command mismatch: use “signtransaction” (and keep “signtx” as alias).
Core calls signtransaction; the mock uses signtx. This will break tests/integration.
-def signtx(args):
+def signtransaction(args):
@@
-parser_signtx = subparsers.add_parser('signtx')
-parser_signtx.add_argument('psbt', metavar='psbt')
-
-parser_signtx.set_defaults(func=signtx)
+parser_signtransaction = subparsers.add_parser('signtransaction')
+parser_signtransaction.add_argument('psbt', metavar='psbt')
+parser_signtransaction.set_defaults(func=signtransaction)
+
+# Back-compat alias
+parser_signtx = subparsers.add_parser('signtx')
+parser_signtx.add_argument('psbt', metavar='psbt')
+parser_signtx.set_defaults(func=signtransaction)Also applies to: 54-68
🤖 Prompt for AI Agents
In test/functional/mocks/signer.py around lines 88-92 (and similarly lines
54-68), the mock CLI command is registered as 'signtx' while Core expects
'signtransaction'; update the parser to register the primary command name as
"signtransaction" and add "signtx" as an alias or alternative name via
add_parser/aliases so both names map to the same handler, and ensure
set_defaults(func=signtx) remains so the existing handler is used for both
names.
| assert_raises_rpc_error(-1, 'Error: restart dashd with -signer=<cmd>', | ||
| self.nodes[0].enumeratesigners | ||
| ) | ||
|
|
||
| # Handle script missing: | ||
| assert_raises_rpc_error(-1, 'execve failed: No such file or directory', | ||
| self.nodes[3].enumeratesigners | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix E128 under-indented continuations and make error assertion OS-portable
- Flake8 E128: align wrapped arguments under the opening parenthesis.
- The missing-signer error message is OS-specific (“execve failed” on POSIX vs CreateProcess/The system cannot find the file on Windows). Relax the assertion to a portable substring.
Apply:
- assert_raises_rpc_error(-1, 'Error: restart dashd with -signer=<cmd>',
- self.nodes[0].enumeratesigners
- )
+ assert_raises_rpc_error(
+ -1,
+ 'Error: restart dashd with -signer=<cmd>',
+ self.nodes[0].enumeratesigners,
+ )
- assert_raises_rpc_error(-1, 'execve failed: No such file or directory',
- self.nodes[3].enumeratesigners
- )
+ err_substr = 'No such file or directory' if platform.system() != 'Windows' else 'find the file'
+ assert_raises_rpc_error(
+ -1,
+ err_substr,
+ self.nodes[3].enumeratesigners,
+ )
- assert_raises_rpc_error(-1, 'RunCommandParseJSON error',
- self.nodes[1].enumeratesigners
- )
+ assert_raises_rpc_error(
+ -1,
+ 'RunCommandParseJSON error',
+ self.nodes[1].enumeratesigners,
+ )
- assert_raises_rpc_error(-1, 'fingerprint not found',
- self.nodes[1].enumeratesigners
- )
+ assert_raises_rpc_error(
+ -1,
+ 'fingerprint not found',
+ self.nodes[1].enumeratesigners,
+ )Also consider quoting the Windows mock path to survive spaces:
- return "py " + path
+ return f'py "{path}"'Also applies to: 60-64, 67-70
🧰 Tools
🪛 Flake8 (7.3.0)
[error] 52-52: continuation line under-indented for visual indent
(E128)
[error] 57-57: continuation line under-indented for visual indent
(E128)
f75e0c1 doc: add external-signer.md (Sjors Provoost) d4b0107 rpc: send: support external signer (Sjors Provoost) 245b445 rpc: signerdisplayaddress (Sjors Provoost) 7ebc7c0 wallet: ExternalSigner: add GetDescriptors method (Sjors Provoost) fc5da52 wallet: add GetExternalSigner() (Sjors Provoost) 259f52c test: external_signer wallet flag is immutable (Sjors Provoost) 2655197 rpc: add external_signer option to createwallet (Sjors Provoost) 2700f09 rpc: signer: add enumeratesigners to list external signers (Sjors Provoost) 07b7c94 rpc: add external signer RPC files (Sjors Provoost) 8ce7767 wallet: add ExternalSignerScriptPubKeyMan (Sjors Provoost) 157ea7c wallet: add external_signer flag (Sjors Provoost) f3e6ce7 test: add external signer test (Sjors Provoost) 8cf543f wallet: add -signer argument for external signer command (Sjors Provoost) f7eb7ec test: framework: add skip_if_no_external_signer (Sjors Provoost) 87a9794 configure: add --enable-external-signer (Sjors Provoost) Pull request description: Big picture overview in [this gist](https://gist.github.com/Sjors/29d06728c685e6182828c1ce9b74483d). This PR lets `bitcoind` call an arbitrary command `-signer=<cmd>`, e.g. a hardware wallet driver, where it can fetch public keys, ask to display an address, and sign a transaction (using PSBT under the hood). It's design to work with https://github.com/bitcoin-core/HWI, which supports multiple hardware wallets. Any command with the same arguments and return values will work. It simplifies the manual procedure described [here](https://github.com/bitcoin-core/HWI/blob/master/docs/bitcoin-core-usage.md). Usage is documented in [doc/external-signer.md]( https://github.com/Sjors/bitcoin/blob/2019/08/hww-box2/doc/external-signer.md), which also describes what protocol a different signer binary should conform to. Use `--enable-external-signer` to opt in, requires Boost::Process: ``` Options used to compile and link: with wallet = yes with gui / qt = no external signer = yes ``` It adds the following RPC methods: * `enumeratesigners`: asks <cmd> for a list of signers (e.g. devices) and their master key fingerprint * `signerdisplayaddress <address>`: asks <cmd> to display an address It enhances the following RPC methods: * `createwallet`: takes an additional `external_signer` argument and fetches keys from device * `send`: automatically sends transaction to device and waits Usage TL&DR: * clone HWI repo somewhere and launch `bitcoind -signer=../HWI/hwi.py` * check if you can see your hardware device: `bitcoin-cli enumeratesigners` * create wallet and auto import keys `bitcoin-cli createwallet "hww" true true "" true true true` * display address on device: `bitcoin-cli signerdisplayaddress ...` * to spend, use `send` RPC and approve transaction on device Prerequisites: - [x] bitcoin#21127 load wallet flags before everything else - [x] bitcoin#21182 remove mostly pointless BOOST_PROCESS macro Potentially useful followups: - GUI support: bitcoin-core/gui#4 - bumpfee support - (automatically) verify (a subset of) keys on the device after import, through message signing ACKs for top commit: laanwj: re-ACK f75e0c1 Tree-SHA512: 7db8afd54762295c1424c3f01d8c587ec256a72f34bd5256e04b21832dabd5dc212be8ab975ae3b67de75259fd569a561491945750492f417111dc7b6641e77f
…ion and typos 8b08d0f build, doc: Fix configure script output indentation and typos (Hennadii Stepanov) Pull request description: This PR is follow up of bitcoin#16546, that breaks the `configure` script output indentation for gui/qt/qr lines: ``` Options used to compile and link: external signer = no multiprocess = no with libs = yes with wallet = yes with sqlite = yes with bdb = yes with gui / qt = yes with qr = yes with zmq = yes with test = yes ... ``` With this PR: ``` Options used to compile and link: external signer = no multiprocess = no with libs = yes with wallet = yes with sqlite = yes with bdb = yes with gui / qt = yes with qr = yes with zmq = yes with test = yes ... ``` Also typos are fixed. ACKs for top commit: Sjors: utACK 8b08d0f vasild: ACK 8b08d0f Tree-SHA512: 46dfcfb754192dbcc080348781327d1714e2f9a696f3ed9252746b426e3afc628d84adb197ba3b8080eacaa6053ccac07e670998930ae92cef8ed713dd457c10
57ff5a4 doc: specify minimum HWI version (Sjors Provoost) 03308b2 rpc: don't require wallet for enumeratesigners (Sjors Provoost) Pull request description: HWI just released 2.0. See https://github.com/bitcoin-core/HWI/releases/tag/2.0.0 As of bitcoin#16546 we already rely on features that are in 2.0 and not in the previous 1.* releases: * `--chain` param This shouldn't be a problem, because HWI 2.0 has been released before we release v22. Misc improvements: * document that HWI 2.0 is required * drop wallet requirement for `enumeratesigners` ACKs for top commit: achow101: Code Review ACK 57ff5a4 Tree-SHA512: 3fb6ba20894e52a116f89525a5f5a1f61d363ccd904e1cffd0e6d095640fc6d2edf0388cd6ae20f83bbc31e5f458255ec090b6e823798d426eba3e45b4336bf9
88d4d5f rpc: add help for enumeratesigners and walletdisplayaddress (Sjors Provoost) b0db187 ci: use --enable-external-signer instead of --with-boost-process (Sjors Provoost) b54b2e7 Move external signer out of wallet module (Sjors Provoost) Pull request description: In addition, this PR enables external signer testing on CI. This PR moves the ExternalSigner class and RPC methods out of the wallet module. The `enumeratesigners` RPC can be used without a wallet since bitcoin#21417. With additional modifications external signers could be used without a wallet in general, e.g. via `signrawtransaction`. The `signerdisplayaddress` RPC is ranamed to `walletdisplayaddress` because it requires wallet context. A future `displayaddress` RPC call without wallet context could take a descriptor argument. This commit fixes a `rpc_help.py` failure when configured with `--disable-wallet`. ACKs for top commit: ryanofsky: Code review ACK 88d4d5f fanquake: ACK 88d4d5f Tree-SHA512: 3242a24e22313aed97eee32a520bfcb1c17495ba32a2b8e06a5e151e2611320e2da5ef35b572d84623af0a49a210d2f9377a2531250868d1a0ccf3e144352a97
c8f469c external_signer: remove ExternalSignerException (fanquake) 9e0b199 external_signer: use const where appropriate (fanquake) aaa4e5a wallet: remove CWallet::GetExternalSigner() (fanquake) 06a0673 external_signer: remove ignore_errors from Enumerate() (fanquake) 8fdbb89 refactor: unify external wallet runtime errors (fanquake) f4652bf refactor: add missing includes to external signer code (fanquake) 54569cc refactor: move all signer code inside ENABLE_EXTERNAL_SIGNER #ifdefs (fanquake) Pull request description: These are a few followups after bitcoin#21467. ACKs for top commit: Sjors: tACK c8f469c instagibbs: utACK bitcoin@c8f469c Tree-SHA512: 3d5ac5df81680075e71e0e4a7595c520d746c3e37f016cf168c1e10da15541ebb1595aecaf2c08575636e9ff77d499644cae53180232b7049cfae0b923106e4e
…allet) 1c4b456 gui: send using external signer (Sjors Provoost) 24815c6 gui: wallet creation detects external signer (Sjors Provoost) 3f845ea node: add externalSigners to interface (Sjors Provoost) 62ac119 gui: display address on external signer (Sjors Provoost) 450cb40 wallet: add displayAddress to interface (Sjors Provoost) eef8d64 gui: create wallet with external signer (Sjors Provoost) 6cdbc83 gui: add external signer path to options dialog (Sjors Provoost) Pull request description: Big picture overview in [this gist](https://gist.github.com/Sjors/29d06728c685e6182828c1ce9b74483d). This PR adds GUI support for external signers, based on the since merged bitcoin#16546 (RPC). The UX isn't amazing - especially the blocking calls - but it works. First we adds a GUI setting for the signer script (e.g. path to HWI): <img width="625" alt="Schermafbeelding 2019-08-05 om 19 32 59" src="https://user-images.githubusercontent.com/10217/62483415-e1ff1680-b7b7-11e9-97ca-8d2ce54ca1cb.png"> Then we add an external signer checkbox to the wallet creation dialog: <img width="374" alt="Schermafbeelding 2019-11-07 om 19 17 23" src="https://user-images.githubusercontent.com/10217/68416387-b57ee000-0194-11ea-9730-127d60273008.png"> It's checked by default if HWI detects a device. It also grabs the name. It then creates a fresh wallet and imports the keys. You can verify an address on the device (blocking...): <img width="673" alt="Schermafbeelding 2019-08-05 om 19 29 22" src="https://user-images.githubusercontent.com/10217/62483560-43bf8080-b7b8-11e9-9902-8a036116dc4b.png"> Sending, including coin selection, Just Works(tm) as long the device is present. ~External signer support is enabled by default when the GUI is configured and Boost::Process is present.~ External signer support remains disabled by default, see bitcoin#21935. ACKs for top commit: achow101: Code Review ACK 1c4b456 hebasto: ACK 1c4b456, tested on Linux Mint 20.1 (Qt 5.12.8) with HWW `2.0.2-rc.1`. promag: Tested ACK 1c4b456 but rebased with e033ca1, with HWI 2.0.2, with Nano S and Nano X. meshcollider: re-code-review ACK 1c4b456 Tree-SHA512: 3503113c5c69d40adb6ce364d8e7cae23ce82d032a00474ba9aeb6202eb70f496ef4a6bf2e623e5171e524ad31ade7941a4e0e89539c64518aaec74f4562d86b
…n unsupported e60cd26 Do not load external signers wallets when unsupported (Andrew Chow) Pull request description: When external signer support is not compiled, do not load external signer wallets. Alternative to bitcoin#22168. ACKs for top commit: promag: Tested ACK e60cd26. meshcollider: Code review ACK e60cd26 Tree-SHA512: aed2d0038f448c2f89c6b48f412b106e63c9ed20e748e69aae21fb58c33fc7e4fa73375a52372c73788669eb2b968a8da6b022c65658fa4484f5bbcf205b1b15
…e #ifdef 2f5bdcb gui: misc external signer fixes and translation hints (Sjors Provoost) d672404 refactor: make ExternalSigner NetworkArg() and m_chain private (Sjors Provoost) 4455145 refactor: reduce #ifdef ENABLE_EXTERNAL_SIGNER usage (Sjors Provoost) 5be90c9 build: enable external signer by default (Sjors Provoost) 7d94530 refactor: clean up external_signer.h includes (Sjors Provoost) fc0eca3 fuzz: fix fuzz binary linking order (Sjors Provoost) Pull request description: This follows the introduction of GUI support in bitcoin-core/gui#4 I don't think we should expect GUI users to self compile. This also enables external signer support by default for RPC users. In addition this PR reduces the number of `#ifdef ENABLE_EXTERNAL_SIGNER`, which also fixes bitcoin#21919. When compiled with `--disable-external-signer` such wallets can't be created in RPC or GUI, but they can be loaded. Attempting any action that calls HWI will trigger an error. Side-note: this PR may or may not (currently) break CI for the GUI repository, as explained here: bitcoin-core/gui#4 (comment) ACKs for top commit: achow101: ACK 2f5bdcb hebasto: re-ACK 2f5bdcb Tree-SHA512: 1b71c5a8bea2be077ee9fa33a01130c957a0cf90951d4b7b04d3d0ef826bb77e474c3963abddfef2e2c1ea99d9c72cd2302d1eb9b5fcb7ba0bd2a625f006aa05
…ocess 67669ab build: Fix Boost Process compatibility with mingw-w64 compiler (Hennadii Stepanov) Pull request description: On master (9c3751a) the cross build for Win64 is broken if configured with `--enable-external-signer`: ``` ... CXX crypto/libbitcoin_crypto_base_a-chacha_poly_aead.o In file included from /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/handles.hpp:11, from /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/used_handles.hpp:17, from /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/async_in.hpp:20, from /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/async.hpp:49, from /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process.hpp:23, from util/system.cpp:9: /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/handle_workaround.hpp:208:51: error: expected ‘)’ before ‘*’ token 208 | typedef ::boost::winapi::NTSTATUS_ (__kernel_entry *nt_system_query_information_p )( | ~ ^~ | ) /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/handle_workaround.hpp:223:51: error: expected ‘)’ before ‘*’ token 223 | typedef ::boost::winapi::NTSTATUS_ (__kernel_entry *nt_query_object_p )( | ~ ^~ | ) /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/handle_workaround.hpp: In function ‘boost::winapi::NTSTATUS_ boost::process::detail::windows::workaround::nt_system_query_information(boost::process::detail::windows::workaround::SYSTEM_INFORMATION_CLASS_, void*, boost::winapi::ULONG_, boost::winapi::PULONG_)’: /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/handle_workaround.hpp:239:12: error: ‘nt_system_query_information_p’ does not name a type; did you mean ‘nt_system_query_information’? 239 | static nt_system_query_information_p f = reinterpret_cast<nt_system_query_information_p>(::boost::winapi::get_proc_address(h, "NtQuerySystemInformation")); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | nt_system_query_information In file included from /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/handles.hpp:11, from /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/used_handles.hpp:17, from /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/async_in.hpp:20, from /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/async.hpp:49, from /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process.hpp:23, from util/system.cpp:9: /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/handle_workaround.hpp:241:14: error: ‘f’ was not declared in this scope 241 | return (*f)(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength); | ^ /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/handle_workaround.hpp: In function ‘boost::winapi::BOOL_ boost::process::detail::windows::workaround::nt_query_object(boost::winapi::HANDLE_, boost::process::detail::windows::workaround::OBJECT_INFORMATION_CLASS_, void*, boost::winapi::ULONG_, boost::winapi::PULONG_)’: /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/handle_workaround.hpp:253:12: error: ‘nt_query_object_p’ does not name a type; did you mean ‘nt_query_object’? 253 | static nt_query_object_p f = reinterpret_cast<nt_query_object_p>(::boost::winapi::get_proc_address(h, "NtQueryObject")); | ^~~~~~~~~~~~~~~~~ | nt_query_object /home/hebasto/GitHub/bitcoin/depends/x86_64-w64-mingw32/include/boost/process/detail/windows/handle_workaround.hpp:255:14: error: ‘f’ was not declared in this scope 255 | return (*f)(Handle, ObjectInformationClass, ObjectInformation, ObjectInformationLength, ReturnLength); | ^ make[2]: *** [Makefile:9906: util/libbitcoin_util_a-system.o] Error 1 make[2]: *** Waiting for unfinished jobs.... CXX crypto/libbitcoin_crypto_base_a-chacha20.o make[2]: Leaving directory '/home/hebasto/GitHub/bitcoin/src' make[1]: *** [Makefile:16141: all-recursive] Error 1 make[1]: Leaving directory '/home/hebasto/GitHub/bitcoin/src' make: *** [Makefile:820: all-recursive] Error 1 ``` The upstream bug: boostorg/process#96 Also see: https://stackoverflow.com/a/59338759 bitcoin#22348 (comment): > [This commit](boostorg/process@7fc41b2), containing the `__kernel_entry` [SAL annotations](https://docs.microsoft.com/en-us/cpp/code-quality/using-sal-annotations-to-reduce-c-cpp-code-defects?view=msvc-160) was included in Boost Process as part of the `1.71.0` release, which broke support for compiling with mingw-w64 because it doesn't define the `__kernel_entry` SAL annotation (but it does define some others, i.e see [`sal.h`](https://github.com/mirror/mingw-w64/blob/master/mingw-w64-headers/include/sal.h)). > > A [commit was made](boostorg/process@d7a721e) to remove the annotations, however, it hasn't made it into either of the two Boost releases that have happened since (1.75.0 & 1.76.0). Meaning that this is currently needed for all versions of Boost process from 1.71.0 onwards. ACKs for top commit: fanquake: ACK 67669ab - thanks for updating this. Tree-SHA512: 5931ca1fb77ce38c042cf5a7556add024ea2386c208bf26c792a8ca4a771d97fac9802c32fa8aa2e3de1ad35f3362d8c066f0a83ee675859d226c602fd0bcf93
…abled without signers a9b9ca8 gui: ensure external signer option remains disabled without signers (Andrew Chow) Pull request description: When no external signers are available, the option to enable external signers should always be disabled. However the encrypt wallet checkbox can erroneously re-enable the external signer checkbox. To avoid this, CreateWalletDialog now stores whether signers were available during setSigners so that future calls to external_signer_checkbox->setEnabled can account for whether signers are available. Fixes dashpay#395 ACKs for top commit: hebasto: ACK a9b9ca8, tested on Linux Mint 20.2 (Qt 5.12.8). Sjors: tACK a9b9ca8 jarolrod: ACK a9b9ca8 Tree-SHA512: 98951bcadc23fce99a66ea2d367c44360989e888c253845a767e1f7085c594562d0f099de4130f4a078c5072aa7806294097d976ee6407291f3d3c5a4a608b44
…gic (stop on first match) d047ed7 external_signer: improve fingerprint matching logic (stop on first match) (Sebastian Falbesoner) Pull request description: The fingerprint matching logic in `ExternalSigner::SignTransaction` currently always iterates all inputs of a PSBT, even after a match has already been found. I guess the reason for that is not that it was not thought of, but rather the fact that breaking out of a nested loop is simply not possible (at least not without adding ugly constructs like gotos or extra state variables). This PR fixes this by using `std::any_of` from C++'s standard library, see http://www.cplusplus.com/reference/algorithm/any_of/ ACKs for top commit: lsilva01: Code Review ACK bitcoin@d047ed7 Sjors: utACK d047ed7 Zero-1729: crACK d047ed7 mjdietzx: Code review ACK d047ed7 hebasto: ACK d047ed7, I have reviewed the code and it looks OK, I agree it can be merged. Tree-SHA512: 447e7c0c6a5b5549a2c09d52e55ba4146302c1a06e4d96de11f6945d09f98c89129cba221202dff7e0718e01a83dd173b9f19b1f02b6be228978f3f6e35d8096
a032fa3 multiprocess: add interfaces::ExternalSigner class (Russell Yanofsky) Pull request description: Add `interfaces::ExternalSigner` class to let signer objects be passed between processes and let signer code run in the original process where the object was created. --- This PR is part of the [process separation project](https://github.com/bitcoin/bitcoin/projects/10). ACKs for top commit: laanwj: Concept and code review ACK a032fa3 hebasto: re-ACK a032fa3 Tree-SHA512: 99a729fb3a64d010e142cc778a9f1f358e58345b77faaf2664de7d2277715d59df3352326e8f0f2a6628038670eaa4556310a549079fb28af6d2eeb05aea1460
5493e92 Check descriptors returned by external signers (sstone) Pull request description: Check that descriptors returned by external signers have been parsed properly when creating a new wallet. See bitcoin#23627 for context. The problem is that parsing an invalid descriptor will return `null` which is not checked for in `CWallet::SetupDescriptorScriptPubKeyMans()`. I'm not completely sure what the best fix is since there several strategies for dealing with errors in the current codebase but the proposed fix is very simple and consistent with other validation checks in `CWallet::SetupDescriptorScriptPubKeyMans()`. ACKs for top commit: jamesob: Code review ACK bitcoin@5493e92 achow101: ACK 5493e92 Tree-SHA512: 63259f4aa519405a86c554b6813efdb741314bdaa18bf005b70ea8bb92a27abc6e2b65f7c584641dc257fc78a6499f42b51b5310c243e611c4663430dccf3d04
b75f4c8 RPC: Return external_signer in getwalletinfo (Kristaps Kaupe) Pull request description: Add `external_signer` to the result object of `getwalletinfo` RPC which indicates whether `WALLET_FLAG_EXTERNAL_SIGNER` flag is set for the wallet. ACKs for top commit: S3RK: utACK b75f4c8 achow101: ACK b75f4c8 prayank23: utACK bitcoin@b75f4c8 brunoerg: utACK b75f4c8 Tree-SHA512: 066ccb97541fd4dc3d9728834645db714a3c8c93ccf29142811af4d79cfb9440a97bbb6c845434a909bc6e1775ef3737fcbb368c1f0582bc63973f6deb17a45f
…or message 7f3a6a9 wallet: Add external-signer-support specific error message (Hennadii Stepanov) Pull request description: On master (5f44c5c) an attempt to load an external signer wallet using Bitcoin Core compiled without external signer support fails with the following log messages: ``` 2022-02-20T19:01:11Z [qt-walletctrl] Using SQLite Version 3.31.1 2022-02-20T19:01:11Z [qt-walletctrl] Using wallet /home/hebasto/.bitcoin/testnet3/wallets/coldcard-0220 2022-02-20T19:01:11Z [qt-walletctrl] init message: Loading wallet… 2022-02-20T19:01:11Z [qt-walletctrl] [coldcard-0220] Error: External signer wallet being loaded without external signer support compiled 2022-02-20T19:01:11Z [qt-walletctrl] [coldcard-0220] Releasing wallet ``` While log messages are good, a message in the GUI window is completely misleading:  This PR fixes this issue:  ACKs for top commit: achow101: ACK 7f3a6a9 kristapsk: ACK 7f3a6a9 brunoerg: crACK 7f3a6a9 Tree-SHA512: a4842751c0ca8a37ccc3ea00503678f6b712a7f53d6cbdc07ce02dcb85ca8a94890d1c2da20307be043faa347747abeba29185c88ba12edd5253bfca56531585
…rnalSigner::SignTransaction 2a22f03 parsing external signer master fingerprint string as bytes instead of caring for lower/upper case in ExternalSigner::SignTransaction (avirgovi) Pull request description: Some external signers scripts may provide master fingerprint in uppercase format. In that case core will fail with `Signer fingerprint 00000000 does not match any of the inputs` as it only works with lowercase format. Even if the fingerprints match, yet one is lowercase the other uppercase. ExternalSigner::SignTransaction is the only place where it is needed IMO, as changing it in other places may break the communication with the external signer (i.e. enumerating with lowercase may not find the device). ACKs for top commit: achow101: ACK 2a22f03 theStack: Code-review ACK 2a22f03 Sjors: utACK 2a22f03 Tree-SHA512: f3d84b83fb0b5e06c405eaf9bf20a2fa864bf4172fd4de113b80b9b9a525a76f2f8cf63031b480358b3a7666023a2aef131fb89ff50448c66df3ed541da10f99
…gner` configure option 8df063e build: Fix help string for `--enable-external-signer` configure option (Hennadii Stepanov) Pull request description: This PR is a follow up of bitcoin#24065 and fixes the help string according to the actual default value https://github.com/bitcoin/bitcoin/blob/816ca01650f4cc66a61ac2f9b0f8b74cd9cd0cf8/configure.ac#L324-L327 ACKs for top commit: kristapsk: cr utACK 8df063e jarolrod: ACK 8df063e Tree-SHA512: ad3f457a53c9238ddd8ded9efd1224e564e6cb9da8b7ff7733a11e32a7daad5c0f6c6223509218f44944a874470cb0d2447897662eaf4e78c763b30785717c50
…ationDialog 742918c qt: hide Create Unsigned button behind an expert mode option (Andrew Chow) 5c3b800 qt: Add Create Unsigned button to SendConfirmationDialog (Andrew Chow) Pull request description: Instead of having different buttons or changing button behavior for making a PSBT, just have SendConfirmationDialog return whether the user wants a PSBT or a broadcasted transaction. Since this dialog is used by both the bumpFeeAction and the SendCoinsDialog, changes to both to support the different behavior is needed. They will check the return value of the SendConfirmationDialog for whether a PSBT needs to be created instead of checking whether private keys are disabled. Strings used in this dialog are being slightly modified to work with both private keys enabled and disabled wallets. Moved from bitcoin#18789 ACKs for top commit: jarolrod: ACK 742918c ryanofsky: Code review ACK 742918c. Just suggested changes since last review. Looks great! hebasto: ACK 742918c, tested on Linux Mint 20.2 (Qt 5.12.8). Tree-SHA512: dd29f4364c7b4f15befe8fe63257b26187918786b005e0f8336183270b1a162680b93f6ced60f0285c6e607c084cc0d24950fc68a8f9c056521ede614041be66
…igning on Windows e2ab9f8 build: disable external signer on Windows (fanquake) Pull request description: This change explicitly disables support for external signing when targeting Windows and OpenBSD. The driver for this is that Boost Process uses boost::filesystem internally, when targeting Windows, which gets in the way of removing our usage of it (bitcoin#20744). While we could adjust bitcoin#20744 to still link against the Boost libs when building for Windows, that would be disappointing, as we wouldn't have cleanly removed the Boost usage we're trying too (including the build infrastructure), and, we'd be in a position where we would be building releases differently depending on the platform, which is something I want to avoid. After discussion with Sjors, Achow and Hebasto, this seemed like a reasonable step to move bitcoin#20744 forward (as-is). Note that support for external signing ([while already being experimental](https://github.com/bitcoin/bitcoin/blob/master/doc/external-signer.md#example-usage)), could be considered even more experimental on Windows. Also, oddly, we have external-signing [explicitly disabled in our Windows (cross-compile) CI](https://github.com/bitcoin/bitcoin/blob/807169e10b4a18324356ed6ee4d69587b96a7c70/ci/test/00_setup_env_win64.sh#L16), it's not clear why this is the case, as, if it's a feature being built into releases, it should be being built and tested in the CI which is most-like the release process. There is an [issue open upstream](boostorg/process#207), in regards to migrating Boost Process to std::filesystem, or having an option to use it. However there hasn't been much discussion since it was opened ~9 months ago. There is another related issue here: klemens-morgenstern/boost-process#164. Resolves bitcoin#24036. ACKs for top commit: Sjors: utACK e2ab9f8 achow101: ACK e2ab9f8 kallewoof: utACK e2ab9f8 hebasto: ACK e2ab9f8, tested on Linux Mint 20.2 (x86_64). Tree-SHA512: 36fcfc0e1a008a8271dc76b8e12e93d3e1d1e528bf668e95a559e9f6fd7d5f031bd7a6a6bc8b9fa9d057b2cd56f9ec8838c7f74e87899bf9a6aeb787afbd112c
UdjinM6
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK 013953b
Issue being fixed or feature implemented
This PR introduce support of external signers (for example, hardware signers such as Ledger) which could be used to sign transactions, using existing feature PSBT.
What was done?
Bitcoin backports from Bitcoin Core v22, v23, v24 to support hardware wallets in Dash Core.
This PR is not the smallest possible set of backports, but it is set that provide working external signer in both CLI and GUI versions of Dash Core. Extra backports to improve user experience are to be done in the next PRs, see #6913
How Has This Been Tested?
Got successfully signed transaction using a repo https://github.com/knst/hwi/ (WIP, see also knst/HWI#1)
Breaking Changes
It introduces multiple new RPC, doesn't seems as any really breaking change
Checklist: